Commit 5c29411e authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'ce-to-ee' into 'master'

CE Upstream - Tuesday

Closes #2247 and gitaly#197

See merge request !1769
parents 51e6fa85 cc59848b
......@@ -46,6 +46,7 @@ eslint-report.html
/public/uploads.*
/public/uploads/
/shared/artifacts/
/spec/javascripts/fixtures/blob/pdf/
/rails_best_practices_output.html
/tags
/tmp/*
......
......@@ -63,16 +63,6 @@ stages:
- redis:alpine
- elasticsearch:5.3
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only:
- /\-(?i)mysql$/
- master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce
- tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee
- //@gitlab/gitlab-ee
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only:
- /mysql/
......@@ -268,29 +258,29 @@ spinach mysql 9 10: *spinach-knapsack-mysql
SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true"
.exec: &exec
.rake-exec: &rake-exec
<<: *ruby-static-analysis
<<: *dedicated-runner
<<: *except-docs
stage: test
script:
- bundle exec $CI_JOB_NAME
- bundle exec rake $CI_JOB_NAME
rubocop:
static-analysis:
<<: *ruby-static-analysis
<<: *dedicated-runner
<<: *except-docs
stage: test
script:
- bundle exec "rubocop --require rubocop-rspec"
- scripts/static-analysis
rake haml_lint: *exec
rake scss_lint: *exec
rake config_lint: *exec
rake brakeman: *exec
rake flay: *exec
license_finder: *exec
rake downtime_check: *exec
downtime_check:
<<: *rake-exec
except:
- master
- tags
- /^[\d-]+-stable(-ee)?$/
- /^docs\/*/
.db-migrate-reset: &db-migrate-reset
stage: test
......@@ -298,12 +288,12 @@ rake downtime_check: *exec
script:
- bundle exec rake db:migrate:reset
rake pg db:migrate:reset:
db:migrate:reset pg:
<<: *db-migrate-reset
<<: *use-pg
<<: *except-docs
rake mysql db:migrate:reset:
db:migrate:reset mysql:
<<: *db-migrate-reset
<<: *use-mysql
<<: *except-docs
......@@ -315,12 +305,12 @@ rake mysql db:migrate:reset:
- bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate
rake pg db:rollback:
db:rollback pg:
<<: *db-rollback
<<: *use-pg
<<: *except-docs
rake mysql db:rollback:
db:rollback mysql:
<<: *db-rollback
<<: *use-mysql
<<: *except-docs
......@@ -342,17 +332,17 @@ rake mysql db:rollback:
paths:
- log/development.log
rake pg db:seed_fu:
db:seed_fu pg:
<<: *db-seed_fu
<<: *use-pg
<<: *except-docs
rake mysql db:seed_fu:
db:seed_fu mysql:
<<: *db-seed_fu
<<: *use-mysql
<<: *except-docs
rake gitlab:assets:compile:
gitlab:assets:compile:
stage: test
<<: *dedicated-runner
<<: *except-docs
......@@ -372,7 +362,7 @@ rake gitlab:assets:compile:
paths:
- webpack-report/
rake karma:
karma:
cache:
paths:
- vendor/ruby
......@@ -391,16 +381,6 @@ rake karma:
paths:
- coverage-javascript/
docs:check:apilint:
image: "phusion/baseimage"
stage: test
<<: *dedicated-runner
cache: {}
dependencies: []
before_script: []
script:
- scripts/lint-doc.sh
docs:check:links:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test
......@@ -416,14 +396,6 @@ docs:check:links:
# Check the internal links
- bundle exec nanoc check internal_links
bundler:check:
stage: test
<<: *dedicated-runner
<<: *ruby-static-analysis
<<: *except-docs
script:
- bundle check
bundler:audit:
stage: test
<<: *ruby-static-analysis
......@@ -456,11 +428,11 @@ bundler:audit:
- . scripts/prepare_build.sh
- bundle exec rake db:migrate
migration pg paths:
migration path pg:
<<: *migration-paths
<<: *use-pg
migration mysql paths:
migration path mysql:
<<: *migration-paths
<<: *use-mysql
......@@ -482,14 +454,6 @@ coverage:
- coverage/index.html
- coverage/assets/
lint:javascript:
<<: *dedicated-runner
<<: *except-docs
stage: test
before_script: []
script:
- yarn run eslint
lint:javascript:report:
<<: *dedicated-runner
<<: *except-docs
......@@ -544,8 +508,8 @@ pages:
<<: *dedicated-runner
dependencies:
- coverage
- rake karma
- rake gitlab:assets:compile
- karma
- gitlab:assets:compile
- lint:javascript:report
script:
- mv public/ .public/
......
Please read this!
Before opening a new issue, make sure to search for keywords in the issues
filtered by the "regression" or "bug" label:
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=regression
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=bug
and verify the issue you're about to submit isn't a duplicate.
Please remove this notice if you're confident your issue isn't a duplicate.
------
### Summary
(Summarize the bug encountered concisely)
......@@ -56,3 +70,5 @@ logs, and code as it's very hard to read otherwise.)
### Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
/label ~bug
Please read this!
Before opening a new issue, make sure to search for keywords in the issues
filtered by the "feature proposal" label:
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=feature+proposal
and verify the issue you're about to submit isn't a duplicate.
Please remove this notice if you're confident your issue isn't a duplicate.
------
### Description
(Include problem, use cases, benefits, and/or goals)
......@@ -15,3 +28,5 @@
3. How does someone use this
During implementation, this can then be copied and used as a starter for the documentation.)
/label ~"feature proposal"
......@@ -4,9 +4,6 @@ entry.
## 9.1.2 (2017-05-01)
- No changes.
- No changes.
- No changes.
- Add index on ci_runners.contacted_at. !10876 (blackst0ne)
- Fix pipeline events description for Slack and Mattermost integration. !10908
- Fixed milestone sidebar showing incorrect number of MRs when collapsed. !10933
......
......@@ -87,14 +87,14 @@ gem 'kaminari', '~> 0.17.0'
gem 'hamlit', '~> 2.6.1'
# Files attachments
gem 'carrierwave', '~> 0.11.0'
gem 'carrierwave', '~> 1.0'
# Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
gem 'fog-core', '~> 1.40'
gem 'fog-core', '~> 1.44'
gem 'fog-google', '~> 0.5'
gem 'fog-local', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
......
......@@ -113,12 +113,10 @@ GEM
capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
carrierwave (0.11.2)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
carrierwave (1.0.0)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
mimemagic (>= 0.3.0)
cause (0.1)
charlock_holmes (0.7.3)
chronic (0.10.2)
......@@ -205,7 +203,7 @@ GEM
erubis (2.7.0)
escape_utils (1.1.1)
eventmachine (1.0.8)
excon (0.52.0)
excon (0.55.0)
execjs (2.6.0)
expression_parser (0.9.0)
extlib (0.9.16)
......@@ -234,12 +232,12 @@ GEM
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
fog-aws (0.11.0)
fog-aws (0.13.0)
fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-core (1.42.0)
fog-core (1.44.1)
builder
excon (~> 0.49)
formatador (~> 0.2)
......@@ -261,9 +259,9 @@ GEM
fog-json (>= 1.0)
fog-xml (>= 0.1)
ipaddress (>= 0.8)
fog-xml (0.1.2)
fog-xml (0.1.3)
fog-core
nokogiri (~> 1.5, >= 1.5.11)
nokogiri (>= 1.5.11, < 2.0.0)
font-awesome-rails (4.7.0.1)
railties (>= 3.2, < 5.1)
foreman (0.78.0)
......@@ -355,7 +353,7 @@ GEM
grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
grpc (1.1.2)
grpc (1.2.5)
google-protobuf (~> 3.1)
googleauth (~> 0.5.1)
gssapi (1.2.0)
......@@ -900,7 +898,7 @@ DEPENDENCIES
bundler-audit (~> 0.5.0)
capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0)
carrierwave (~> 0.11.0)
carrierwave (~> 1.0)
charlock_holmes (~> 0.7.3)
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
......@@ -929,7 +927,7 @@ DEPENDENCIES
ffaker (~> 2.4)
flay (~> 2.8.0)
fog-aws (~> 0.9)
fog-core (~> 1.40)
fog-core (~> 1.44)
fog-google (~> 0.5)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
......
app/assets/images/ci_favicons/favicon_status_canceled.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_canceled.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_canceled.ico
app/assets/images/ci_favicons/favicon_status_canceled.ico
app/assets/images/ci_favicons/favicon_status_canceled.ico
app/assets/images/ci_favicons/favicon_status_canceled.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_created.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_created.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_created.ico
app/assets/images/ci_favicons/favicon_status_created.ico
app/assets/images/ci_favicons/favicon_status_created.ico
app/assets/images/ci_favicons/favicon_status_created.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_failed.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_failed.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_failed.ico
app/assets/images/ci_favicons/favicon_status_failed.ico
app/assets/images/ci_favicons/favicon_status_failed.ico
app/assets/images/ci_favicons/favicon_status_failed.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_manual.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_manual.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_manual.ico
app/assets/images/ci_favicons/favicon_status_manual.ico
app/assets/images/ci_favicons/favicon_status_manual.ico
app/assets/images/ci_favicons/favicon_status_manual.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_not_found.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_not_found.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_not_found.ico
app/assets/images/ci_favicons/favicon_status_not_found.ico
app/assets/images/ci_favicons/favicon_status_not_found.ico
app/assets/images/ci_favicons/favicon_status_not_found.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_pending.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_pending.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_pending.ico
app/assets/images/ci_favicons/favicon_status_pending.ico
app/assets/images/ci_favicons/favicon_status_pending.ico
app/assets/images/ci_favicons/favicon_status_pending.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_running.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_running.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_running.ico
app/assets/images/ci_favicons/favicon_status_running.ico
app/assets/images/ci_favicons/favicon_status_running.ico
app/assets/images/ci_favicons/favicon_status_running.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_skipped.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_skipped.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_skipped.ico
app/assets/images/ci_favicons/favicon_status_skipped.ico
app/assets/images/ci_favicons/favicon_status_skipped.ico
app/assets/images/ci_favicons/favicon_status_skipped.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_success.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_success.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_success.ico
app/assets/images/ci_favicons/favicon_status_success.ico
app/assets/images/ci_favicons/favicon_status_success.ico
app/assets/images/ci_favicons/favicon_status_success.ico
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/ci_favicons/favicon_status_warning.ico

5.3 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_warning.ico

4.19 KB | W: | H:

app/assets/images/ci_favicons/favicon_status_warning.ico
app/assets/images/ci_favicons/favicon_status_warning.ico
app/assets/images/ci_favicons/favicon_status_warning.ico
app/assets/images/ci_favicons/favicon_status_warning.ico
  • 2-up
  • Swipe
  • Onion skin
/* eslint-disable no-new */
import Vue from 'vue';
import PDFLab from 'vendor/pdflab';
import workerSrc from 'vendor/pdf.worker';
Vue.use(PDFLab, {
workerSrc,
});
import pdfLab from '../../pdf/index.vue';
export default () => {
const el = document.getElementById('js-pdf-viewer');
......@@ -20,6 +15,9 @@ export default () => {
pdf: el.dataset.endpoint,
};
},
components: {
pdfLab,
},
methods: {
onLoad() {
this.loading = false;
......
......@@ -6,7 +6,7 @@ export default class BlobViewer {
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
this.simpleViewer = document.querySelector('.blob-viewer[data-type="simple"]');
this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]');
this.$blobContentHolder = $('#blob-content-holder');
this.$fileHolder = $('.file-holder');
let initialViewerName = document.querySelector('.blob-viewer:not(.hidden)').getAttribute('data-type');
......@@ -82,7 +82,7 @@ export default class BlobViewer {
viewer.setAttribute('data-loaded', 'true');
this.$blobContentHolder.trigger('highlight:line');
this.$fileHolder.trigger('highlight:line');
this.toggleCopyButtonState();
});
......
......@@ -47,12 +47,12 @@ import ProjectsList from './projects_list';
import ApproversSelect from './approvers_select';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
import Landing from './landing';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import UserCallout from './user_callout';
import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags';
import ShortcutsWiki from './shortcuts_wiki';
import BlobViewer from './blob/viewer/index';
import GeoNodes from './geo_nodes';
import ServiceDeskRoot from './projects/settings_service_desk/service_desk_root';
......@@ -154,8 +154,19 @@ const ShortcutsBlob = require('./shortcuts_blob');
new ProjectsList();
break;
case 'dashboard:groups:index':
new GroupsList();
break;
case 'explore:groups:index':
new GroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing');
if (!landingElement) break;
const exploreGroupsLanding = new Landing(
landingElement,
landingElement.querySelector('.dismiss-button'),
'explore_groups_landing_dismissed',
);
exploreGroupsLanding.toggle();
break;
case 'projects:milestones:new':
case 'projects:milestones:edit':
......@@ -378,6 +389,10 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'users:show':
new UserCallout();
break;
case 'snippets:show':
new LineHighlighter();
new BlobViewer();
break;
}
switch (path.first()) {
case 'sessions':
......@@ -460,6 +475,8 @@ const ShortcutsBlob = require('./shortcuts_blob');
shortcut_handler = new ShortcutsNavigation();
if (path[2] === 'show') {
new ZenMode();
new LineHighlighter();
new BlobViewer();
}
break;
case 'labels':
......
import Cookies from 'js-cookie';
class Landing {
constructor(landingElement, dismissButton, cookieName) {
this.landingElement = landingElement;
this.cookieName = cookieName;
this.dismissButton = dismissButton;
this.eventWrapper = {};
}
toggle() {
const isDismissed = this.isDismissed();
this.landingElement.classList.toggle('hidden', isDismissed);
if (!isDismissed) this.addEvents();
}
addEvents() {
this.eventWrapper.dismissLanding = this.dismissLanding.bind(this);
this.dismissButton.addEventListener('click', this.eventWrapper.dismissLanding);
}
removeEvents() {
this.dismissButton.removeEventListener('click', this.eventWrapper.dismissLanding);
}
dismissLanding() {
this.landingElement.classList.add('hidden');
Cookies.set(this.cookieName, 'true', { expires: 365 });
}
isDismissed() {
return Cookies.get(this.cookieName) === 'true';
}
}
export default Landing;
......@@ -57,9 +57,9 @@ require('vendor/jquery.scrollTo');
}
LineHighlighter.prototype.bindEvents = function() {
const $blobContentHolder = $('#blob-content-holder');
$blobContentHolder.on('click', 'a[data-line-number]', this.clickHandler);
$blobContentHolder.on('highlight:line', this.highlightHash);
const $fileHolder = $('.file-holder');
$fileHolder.on('click', 'a[data-line-number]', this.clickHandler);
$fileHolder.on('highlight:line', this.highlightHash);
};
LineHighlighter.prototype.highlightHash = function() {
......
<template>
<div class="pdf-viewer" v-if="hasPDF">
<page v-for="(page, index) in pages"
:key="index"
:v-if="!loading"
:page="page"
:number="index + 1" />
</div>
</template>
<script>
import pdfjsLib from 'pdfjs-dist';
import workerSrc from 'vendor/pdf.worker';
import page from './page/index.vue';
export default {
props: {
pdf: {
type: [String, Uint8Array],
required: true,
},
},
data() {
return {
loading: false,
pages: [],
};
},
components: { page },
watch: { pdf: 'load' },
computed: {
document() {
return typeof this.pdf === 'string' ? this.pdf : { data: this.pdf };
},
hasPDF() {
return this.pdf && this.pdf.length > 0;
},
},
methods: {
load() {
this.pages = [];
return pdfjsLib.getDocument(this.document)
.then(this.renderPages)
.then(() => this.$emit('pdflabload'))
.catch(error => this.$emit('pdflaberror', error))
.then(() => { this.loading = false; });
},
renderPages(pdf) {
const pagePromises = [];
this.loading = true;
for (let num = 1; num <= pdf.numPages; num += 1) {
pagePromises.push(
pdf.getPage(num).then(p => this.pages.push(p)),
);
}
return Promise.all(pagePromises);
},
},
mounted() {
pdfjsLib.PDFJS.workerSrc = workerSrc;
if (this.hasPDF) this.load();
},
};
</script>
<style>
.pdf-viewer {
background: url('./assets/img/bg.gif');
display: flex;
flex-flow: column nowrap;
}
</style>
<template>
<canvas
class="pdf-page"
ref="canvas"
:data-page="number" />
</template>
<script>
export default {
props: {
page: {
type: Object,
required: true,
},
number: {
type: Number,
required: true,
},
},
data() {
return {
scale: 4,
rendering: false,
};
},
computed: {
viewport() {
return this.page.getViewport(this.scale);
},
context() {
return this.$refs.canvas.getContext('2d');
},
renderContext() {
return {
canvasContext: this.context,
viewport: this.viewport,
};
},
},
mounted() {
this.$refs.canvas.height = this.viewport.height;
this.$refs.canvas.width = this.viewport.width;
this.rendering = true;
this.page.render(this.renderContext)
.then(() => { this.rendering = false; })
.catch(error => this.$emit('pdflaberror', error));
},
};
</script>
<style>
.pdf-page {
margin: 8px auto 0 auto;
border-top: 1px #ddd solid;
border-bottom: 1px #ddd solid;
width: 100%;
}
.pdf-page:first-child {
margin-top: 0px;
border-top: 0px;
}
.pdf-page:last-child {
margin-bottom: 0px;
border-bottom: 0px;
}
</style>
......@@ -227,8 +227,8 @@
.award-control-icon-positive,
.award-control-icon-super-positive {
position: absolute;
left: 7px;
bottom: 9px;
left: 11px;
bottom: 7px;
opacity: 0;
@include transition(opacity, transform);
}
......
......@@ -254,6 +254,63 @@
padding: 10px 0;
}
.landing {
margin-bottom: $gl-padding;
overflow: hidden;
display: flex;
position: relative;
border: 1px solid $blue-300;
border-radius: $border-radius-default;
background-color: $blue-25;
justify-content: center;
.dismiss-button {
position: absolute;
right: 6px;
top: 6px;
cursor: pointer;
color: $blue-300;
z-index: 1;
border: none;
background-color: transparent;
&:hover,
&:focus {
border: none;
color: $blue-400;
}
}
.svg-container {
align-self: center;
}
.inner-content {
text-align: left;
white-space: nowrap;
h4 {
color: $gl-text-color;
font-size: 17px;
}
p {
color: $gl-text-color;
margin-bottom: $gl-padding;
}
}
@media (max-width: $screen-sm-min) {
flex-direction: column;
.inner-content {
white-space: normal;
padding: 0 28px;
text-align: center;
}
}
}
.empty-state {
margin: 100px 0 0;
......
......@@ -428,6 +428,11 @@ table {
}
}
.bordered-box {
border: 1px solid $border-color;
border-radius: $border-radius-default;
}
.str-truncated {
&-60 {
@include str-truncated(60%);
......
......@@ -61,11 +61,13 @@
.file-content {
background: $white-light;
&.image_file {
&.image_file,
&.video {
background: $file-image-bg;
text-align: center;
img {
img,
video {
padding: 20px;
max-width: 80%;
}
......
......@@ -120,6 +120,10 @@
// Ensure that image does not exceed viewport
max-height: calc(100vh - 100px);
}
table {
@include markdown-table;
}
}
.toolbar-group {
......
......@@ -12,6 +12,13 @@
max-width: $max_width;
}
/*
* Mixin for markdown tables
*/
@mixin markdown-table {
width: auto;
}
/*
* Base mixin for lists in GitLab
*/
......
......@@ -200,6 +200,7 @@
.header-content {
flex: 1;
line-height: 1.8;
a {
color: $gl-text-color;
......
......@@ -93,11 +93,6 @@
top: $gl-padding-top;
}
.bordered-box {
border: 1px solid $border-color;
border-radius: $border-radius-default;
}
.content-list {
li {
padding: 18px $gl-padding $gl-padding;
......@@ -139,42 +134,9 @@
}
}
.landing {
margin-bottom: $gl-padding;
overflow: hidden;
.dismiss-icon {
position: absolute;
right: $cycle-analytics-box-padding;
cursor: pointer;
color: $cycle-analytics-dismiss-icon-color;
}
.svg-container {
text-align: center;
svg {
width: 136px;
height: 136px;
}
}
.inner-content {
@media (max-width: $screen-xs-max) {
padding: 0 28px;
text-align: center;
}
h4 {
color: $gl-text-color;
font-size: 17px;
}
p {
color: $cycle-analytics-box-text-color;
margin-bottom: $gl-padding;
}
}
.landing svg {
width: 136px;
height: 136px;
}
.fa-spinner {
......
......@@ -136,3 +136,26 @@ table.pipeline-project-metrics tr td {
color: $gl-text-color-secondary;
margin-top: 10px;
}
.explore-groups.landing {
margin-top: 10px;
.inner-content {
padding: 0;
p {
margin: 7px 0 0;
max-width: 480px;
padding: 0 $gl-padding;
@media (max-width: $screen-sm-min) {
margin: 0 auto;
}
}
}
svg {
width: 62px;
height: 50px;
}
}
......@@ -101,11 +101,16 @@ ul.related-merge-requests > li {
}
}
.merge-request-ci-status {
.merge-request-ci-status,
.related-merge-requests {
.ci-status-link {
display: block;
margin-top: 3px;
margin-right: 5px;
}
svg {
margin-right: 4px;
position: relative;
top: 1px;
display: block;
}
}
......
......@@ -97,6 +97,10 @@ ul.notes {
padding-left: 1.3em;
}
}
table {
@include markdown-table;
}
}
}
......
......@@ -159,3 +159,9 @@ ul.wiki-pages-list.content-list {
padding: 5px 0;
}
}
.wiki {
table {
@include markdown-table;
}
}
class Admin::HooksController < Admin::ApplicationController
before_action :hook, only: :edit
def index
@hooks = SystemHook.all
@hook = SystemHook.new
......@@ -15,15 +17,25 @@ class Admin::HooksController < Admin::ApplicationController
end
end
def edit
end
def update
if hook.update_attributes(hook_params)
flash[:notice] = 'System hook was successfully updated.'
redirect_to admin_hooks_path
else
render 'edit'
end
end
def destroy
@hook = SystemHook.find(params[:id])
@hook.destroy
hook.destroy
redirect_to admin_hooks_path
end
def test
@hook = SystemHook.find(params[:hook_id])
data = {
event_name: "project_create",
name: "Ruby",
......@@ -32,11 +44,17 @@ class Admin::HooksController < Admin::ApplicationController
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
@hook.execute(data, 'system_hooks')
hook.execute(data, 'system_hooks')
redirect_back_or_default
end
private
def hook
@hook ||= SystemHook.find(params[:id])
end
def hook_params
params.require(:hook).permit(
:enable_ssl_verification,
......
......@@ -122,6 +122,10 @@ class ApplicationController < ActionController::Base
end
end
def respond_422
head :unprocessable_entity
end
def no_cache_headers
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
response.headers["Pragma"] = "no-cache"
......
......@@ -14,4 +14,8 @@ module RendersBlob
html: view_to_html_string("projects/blob/_viewer", viewer: viewer, load_asynchronously: false)
}
end
def override_max_blob_size(blob)
blob.override_max_size! if params[:override_max_size] == 'true'
end
end
......@@ -38,6 +38,7 @@ module ServiceParams
:new_issue_url,
:notify,
:notify_only_broken_pipelines,
:notify_only_default_branch,
:password,
:priority,
:project_key,
......
......@@ -35,7 +35,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def show
@blob.override_max_size! if params[:override_max_size] == 'true'
override_max_blob_size(@blob)
respond_to do |format|
format.html do
......
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry, :play]
before_action :authorize_read_build!, only: [:index, :show, :status, :raw, :trace]
before_action :authorize_update_build!, except: [:index, :show, :status, :raw, :trace]
layout 'project'
......@@ -60,20 +60,22 @@ class Projects::BuildsController < Projects::ApplicationController
end
def retry
return render_404 unless @build.retryable?
return respond_422 unless @build.retryable?
build = Ci::Build.retry(@build, current_user)
redirect_to build_path(build)
end
def play
return render_404 unless @build.playable?
return respond_422 unless @build.playable?
build = @build.play(current_user)
redirect_to build_path(build)
end
def cancel
return respond_422 unless @build.cancelable?
@build.cancel
redirect_to build_path(@build)
end
......@@ -85,9 +87,12 @@ class Projects::BuildsController < Projects::ApplicationController
end
def erase
@build.erase(erased_by: current_user)
redirect_to namespace_project_build_path(project.namespace, project, @build),
if @build.erase(erased_by: current_user)
redirect_to namespace_project_build_path(project.namespace, project, @build),
notice: "Build has been successfully erased!"
else
respond_422
end
end
def raw
......
class Projects::HooksController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project!
before_action :hook, only: :edit
respond_to :html
......@@ -17,6 +18,18 @@ class Projects::HooksController < Projects::ApplicationController
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end
def edit
end
def update
if hook.update_attributes(hook_params)
flash[:notice] = 'Hook was successfully updated.'
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
else
render 'edit'
end
end
def test
if !@project.empty_repo?
status, message = TestHookService.new.execute(hook, current_user)
......
......@@ -5,7 +5,7 @@ module Projects
before_action :authorize_admin_project!
layout "project_settings"
def show
@hooks = @project.hooks
@hook = ProjectHook.new
......
......@@ -3,6 +3,7 @@ class Projects::SnippetsController < Projects::ApplicationController
include ToggleAwardEmoji
include SpammableActions
include SnippetsActions
include RendersBlob
before_action :module_enabled
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
......@@ -55,11 +56,23 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def show
@note = @project.notes.new(noteable: @snippet)
@noteable = @snippet
@discussions = @snippet.discussions
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes))
blob = @snippet.blob
override_max_blob_size(blob)
respond_to do |format|
format.html do
@note = @project.notes.new(noteable: @snippet)
@noteable = @snippet
@discussions = @snippet.discussions
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes))
render 'show'
end
format.json do
render_blob_json(blob)
end
end
end
def destroy
......
......@@ -4,6 +4,7 @@ class SnippetsController < ApplicationController
include SpammableActions
include SnippetsActions
include MarkdownPreview
include RendersBlob
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
......@@ -61,10 +62,23 @@ class SnippetsController < ApplicationController
end
def show
blob = @snippet.blob
override_max_blob_size(blob)
@noteable = @snippet
@discussions = @snippet.discussions
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes))
respond_to do |format|
format.html do
render 'show'
end
format.json do
render_blob_json(blob)
end
end
end
def destroy
......
if Rails.env.test?
class UnicornTestController < ActionController::Base
def pid
render plain: Process.pid.to_s
end
def kill
Process.kill(params[:signal], Process.pid)
render plain: 'Bye!'
end
end
end
......@@ -119,7 +119,15 @@ module BlobHelper
end
def blob_raw_url
namespace_project_raw_path(@project.namespace, @project, @id)
if @snippet
if @snippet.project_id
raw_namespace_project_snippet_path(@project.namespace, @project, @snippet)
else
raw_snippet_path(@snippet)
end
elsif @blob
namespace_project_raw_path(@project.namespace, @project, @id)
end
end
# SVGs can contain malicious JavaScript; only include whitelisted
......@@ -209,11 +217,21 @@ module BlobHelper
end
def copy_blob_source_button(blob)
return unless blob.rendered_as_text?(ignore_errors: false)
clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}']", class: "btn btn-sm js-copy-blob-source-btn", title: "Copy source to clipboard")
end
def open_raw_file_button(path)
link_to icon('file-code-o'), path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw', data: { container: 'body' }
def open_raw_blob_button(blob)
if blob.raw_binary?
icon = icon('download')
title = 'Download'
else
icon = icon('file-code-o')
title = 'Open raw'
end
link_to icon, blob_raw_url, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' }
end
def blob_render_error_reason(viewer)
......
......@@ -10,11 +10,12 @@ module EventsHelper
'deleted' => 'icon_trash_o'
}.freeze
def link_to_author(event)
def link_to_author(event, self_added: false)
author = event.author
if author
link_to author.name, user_path(author.username), title: author.name
name = self_added ? 'You' : author.name
link_to name, user_path(author.username), title: name
else
event.author_name
end
......
......@@ -74,7 +74,7 @@ module MarkupHelper
context[:project] ||= @project
html = markdown_unsafe(text, context)
banzai_postprocess(html, context)
prepare_for_rendering(html, context)
end
def markdown_field(object, field)
......@@ -82,13 +82,13 @@ module MarkupHelper
return '' unless object.present?
html = Banzai.render_field(object, field)
banzai_postprocess(html, object.banzai_render_context(field))
prepare_for_rendering(html, object.banzai_render_context(field))
end
def markup(file_name, text, context = {})
context[:project] ||= @project
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
banzai_postprocess(html, context)
prepare_for_rendering(html, context)
end
def render_wiki_content(wiki_page)
......@@ -107,14 +107,14 @@ module MarkupHelper
wiki_page.formatted_content.html_safe
end
banzai_postprocess(html, context)
prepare_for_rendering(html, context)
end
def markup_unsafe(file_name, text, context = {})
return '' unless text.present?
if gitlab_markdown?(file_name)
Hamlit::RailsHelpers.preserve(markdown_unsafe(text, context))
markdown_unsafe(text, context)
elsif asciidoc?(file_name)
asciidoc_unsafe(text)
elsif plain?(file_name)
......@@ -225,8 +225,7 @@ module MarkupHelper
Gitlab::OtherMarkup.render(file_name, text)
end
# Calls Banzai.post_process with some common context options
def banzai_postprocess(html, context = {})
def prepare_for_rendering(html, context = {})
return '' unless html.present?
context.merge!(
......@@ -239,7 +238,9 @@ module MarkupHelper
requested_path: @path
)
Banzai.post_process(html, context)
html = Banzai.post_process(html, context)
Hamlit::RailsHelpers.preserve(html)
end
extend self
......
module MergeRequestsHelper
def new_mr_path_from_push_event(event)
target_project = event.project.forked_from_project || event.project
target_project = event.project.default_merge_request_target
new_namespace_project_merge_request_path(
event.project.namespace,
event.project,
......@@ -141,6 +141,10 @@ module MergeRequestsHelper
end
end
def target_projects(project)
[project, project.default_merge_request_target].uniq
end
def merge_request_button_visibility(merge_request, closed)
return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?) || merge_request.closed_without_fork?
end
......
......@@ -13,13 +13,13 @@ module TodosHelper
def todo_action_name(todo)
case todo.action
when Todo::ASSIGNED then 'assigned you'
when Todo::MENTIONED then 'mentioned you on'
when Todo::ASSIGNED then todo.self_added? ? 'assigned' : 'assigned you'
when Todo::MENTIONED then "mentioned #{todo_action_subject(todo)} on"
when Todo::BUILD_FAILED then 'The build failed for'
when Todo::MARKED then 'added a todo for'
when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
when Todo::APPROVAL_REQUIRED then "set #{todo_action_subject(todo)} as an approver for"
when Todo::UNMERGEABLE then 'Could not merge'
when Todo::DIRECTLY_ADDRESSED then 'directly addressed you on'
when Todo::DIRECTLY_ADDRESSED then "directly addressed #{todo_action_subject(todo)} on"
end
end
......@@ -148,6 +148,10 @@ module TodosHelper
private
def todo_action_subject(todo)
todo.self_added? ? 'yourself' : 'you'
end
def show_todo_state?(todo)
(todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state)
end
......
......@@ -27,6 +27,8 @@ class Blob < SimpleDelegator
BlobViewer::Image,
BlobViewer::Sketch,
BlobViewer::Video,
BlobViewer::PDF,
BlobViewer::BinarySTL,
......
module BlobViewer
class Video < Base
include Rich
include ClientSide
self.partial_name = 'video'
self.extensions = UploaderHelper::VIDEO_EXT
self.binary = true
self.switcher_icon = 'film'
self.switcher_title = 'video'
end
end
......@@ -34,6 +34,7 @@ class Label < ActiveRecord::Base
scope :templates, -> { where(template: true) }
scope :with_title, ->(title) { where(title: title) }
scope :on_project_boards, ->(project_id) { joins(lists: :board).merge(List.movable).where(boards: { project_id: project_id }) }
def self.prioritized(project)
joins(:priorities)
......
......@@ -157,6 +157,11 @@ class Member < ActiveRecord::Base
def add_users(source, users, access_level, current_user: nil, expires_at: nil)
return [] unless users.present?
# Collect all user ids into separate array
# so we can use single sql query to get user objects
user_ids = users.select { |user| user =~ /\A\d+\Z/ }
users = users - user_ids + User.where(id: user_ids)
self.transaction do
users.map do |user|
add_user(
......
......@@ -106,6 +106,7 @@ class MergeRequest < ActiveRecord::Base
validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
validate :validate_fork, unless: :closed_without_fork?
validate :validate_approvals_before_merge
validate :validate_target_project, on: :create
scope :by_source_or_target_branch, ->(branch_name) do
where("source_branch = :branch OR target_branch = :branch", branch: branch_name)
......@@ -337,6 +338,12 @@ class MergeRequest < ActiveRecord::Base
end
end
def validate_target_project
return true if target_project.merge_requests_enabled?
errors.add :base, 'Target project has disabled merge requests'
end
def validate_fork
return true unless target_project && source_project
return true if target_project == source_project
......
......@@ -1571,6 +1571,14 @@ class Project < ActiveRecord::Base
namespace_id_changed?
end
def default_merge_request_target
if forked_from_project&.merge_requests_enabled?
forked_from_project
else
self
end
end
alias_method :name_with_namespace, :full_name
alias_method :human_name, :full_name
alias_method :path_with_namespace, :full_path
......
......@@ -22,7 +22,7 @@ class ChatNotificationService < Service
end
def can_test?
super && valid?
valid?
end
def self.supported_events
......
......@@ -1035,15 +1035,13 @@ class Repository
end
def is_ancestor?(ancestor_id, descendant_id)
# NOTE: This feature is intentionally disabled until
# https://gitlab.com/gitlab-org/gitlab-ce/issues/30586 is resolved
# Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
# if is_enabled
# raw_repository.is_ancestor?(ancestor_id, descendant_id)
# else
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
# end
# end
Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
if is_enabled
raw_repository.is_ancestor?(ancestor_id, descendant_id)
else
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
end
end
end
def empty_repo?
......
......@@ -132,7 +132,7 @@ class Service < ActiveRecord::Base
end
def can_test?
!project.empty_repo?
true
end
# reason why service cannot be tested
......
class Snippet < ActiveRecord::Base
include Gitlab::VisibilityLevel
include Linguist::BlobHelper
include CacheMarkdownField
include Noteable
include Participable
......@@ -88,47 +87,26 @@ class Snippet < ActiveRecord::Base
]
end
def data
content
def blob
@blob ||= Blob.decorate(SnippetBlob.new(self), nil)
end
def hook_attrs
attributes
end
def size
0
end
def file_name
super.to_s
end
# alias for compatibility with blobs and highlighting
def path
file_name
end
def name
file_name
end
def sanitized_file_name
file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '')
end
def mode
nil
end
def visibility_level_field
:visibility_level
end
def no_highlighting?
content.lines.count > 1000
end
def notes_with_associations
notes.includes(:author)
end
......
class SnippetBlob
include Linguist::BlobHelper
attr_reader :snippet
def initialize(snippet)
@snippet = snippet
end
delegate :id, to: :snippet
def name
snippet.file_name
end
alias_method :path, :name
def size
data.bytesize
end
def data
snippet.content
end
def rendered_markup
return unless Gitlab::MarkupHelper.gitlab_markdown?(name)
Banzai.render_field(snippet, :content)
end
def mode
nil
end
def binary?
false
end
def load_all_data!(repository)
# No-op
end
def lfs_pointer?
false
end
def lfs_oid
nil
end
def lfs_size
nil
end
def truncated?
false
end
end
......@@ -84,6 +84,10 @@ class Todo < ActiveRecord::Base
action == BUILD_FAILED
end
def assigned?
action == ASSIGNED
end
def action_name
ACTION_NAMES[action]
end
......@@ -117,6 +121,14 @@ class Todo < ActiveRecord::Base
end
end
def self_added?
author == user
end
def self_assigned?
assigned? && self_added?
end
private
def keep_around_commit
......
......@@ -1101,11 +1101,13 @@ class User < ActiveRecord::Base
User.find_by_email(s)
end
scope.create(
user = scope.build(
username: username,
email: email,
&creation_block
)
user.save(validate: false)
user
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
......
......@@ -7,6 +7,9 @@ class StatusEntity < Grape::Entity
expose :details_path
expose :favicon do |status|
ActionController::Base.helpers.image_path(File.join('ci_favicons', "#{status.favicon}.ico"))
dir = 'ci_favicons'
dir = File.join(dir, 'dev') if Rails.env.development?
ActionController::Base.helpers.image_path(File.join(dir, "#{status.favicon}.ico"))
end
end
......@@ -61,7 +61,7 @@ module Boards
if moving_to_list.movable?
moving_from_list.label_id
else
project.boards.joins(:lists).merge(List.movable).pluck(:label_id)
Label.on_project_boards(project.id).pluck(:label_id)
end
Array(label_ids).compact
......
......@@ -28,7 +28,7 @@ module MergeRequests
def find_target_project
return target_project if target_project.present? && can?(current_user, :read_project, target_project)
project.forked_from_project || project
project.default_merge_request_target
end
def find_target_branch
......
......@@ -353,6 +353,28 @@ module SlashCommands
@updates[:weight] = nil
end
desc 'Move issue from one column of the board to another'
params '~"Target column"'
condition do
issuable.is_a?(Issue) &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable) &&
issuable.project.boards.count == 1
end
command :board_move do |target_list_name|
label_ids = find_label_ids(target_list_name)
if label_ids.size == 1
label_id = label_ids.first
# Ensure this label corresponds to a list on the board
next unless Label.on_project_boards(issuable.project_id).where(id: label_id).exists?
@updates[:remove_label_ids] =
issuable.labels.on_project_boards(issuable.project_id).where.not(id: label_id).pluck(:id)
@updates[:add_label_ids] = [label_id]
end
end
def find_label_ids(labels_param)
label_ids_by_reference = extract_references(labels_param, :label).map(&:id)
labels_ids_by_name = LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split).execute.select(:id)
......
......@@ -86,6 +86,12 @@
= container_reg
%span.light.pull-right
= boolean_to_icon Gitlab.config.registry.enabled
- gitlab_pages = 'GitLab Pages'
- gitlab_pages_enabled = Gitlab.config.pages.enabled
%p{ "aria-label" => "#{gitlab_pages}: status " + (gitlab_pages_enabled ? "on" : "off") }
= gitlab_pages
%span.light.pull-right
= boolean_to_icon gitlab_pages_enabled
.col-md-4
%h4
......
= form_errors(hook)
.form-group
= form.label :url, 'URL', class: 'control-label'
.col-sm-10
= form.text_field :url, class: 'form-control'
.form-group
= form.label :token, 'Secret Token', class: 'control-label'
.col-sm-10
= form.text_field :token, class: 'form-control'
%p.help-block
Use this token to validate received payloads
.form-group
= form.label :url, 'Trigger', class: 'control-label'
.col-sm-10.prepend-top-10
%div
System hook will be triggered on set of events like creating project
or adding ssh key. But you can also enable extra triggers like Push events.
.prepend-top-default
= form.check_box :push_events, class: 'pull-left'
.prepend-left-20
= form.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
%div
= form.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= form.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
.form-group
= form.label :enable_ssl_verification, 'SSL verification', class: 'control-label checkbox'
.col-sm-10
.checkbox
= form.label :enable_ssl_verification do
= form.check_box :enable_ssl_verification
%strong Enable SSL verification
- page_title 'Edit System Hook'
%h3.page-title
Edit System Hook
%p.light
#{link_to 'System hooks ', help_page_path('system_hooks/system_hooks'), class: 'vlink'} can be
used for binding events when GitLab creates a User or Project.
%hr
= form_for @hook, as: :hook, url: admin_hook_path, html: { class: 'form-horizontal' } do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
= f.submit 'Save changes', class: 'btn btn-create'
- page_title "System Hooks"
- page_title 'System Hooks'
%h3.page-title
System hooks
%p.light
#{link_to "System hooks ", help_page_path("system_hooks/system_hooks"), class: "vlink"} can be
#{link_to 'System hooks ', help_page_path('system_hooks/system_hooks'), class: 'vlink'} can be
used for binding events when GitLab creates a User or Project.
%hr
= form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f|
= form_errors(@hook)
.form-group
= f.label :url, 'URL', class: 'control-label'
.col-sm-10
= f.text_field :url, class: 'form-control'
.form-group
= f.label :token, 'Secret Token', class: 'control-label'
.col-sm-10
= f.text_field :token, class: 'form-control'
%p.help-block
Use this token to validate received payloads
.form-group
= f.label :url, "Trigger", class: 'control-label'
.col-sm-10.prepend-top-10
%div
System hook will be triggered on set of events like creating project
or adding ssh key. But you can also enable extra triggers like Push events.
.prepend-top-default
= f.check_box :push_events, class: 'pull-left'
.prepend-left-20
= f.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
%div
= f.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= f.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
.form-group
= f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox'
.col-sm-10
.checkbox
= f.label :enable_ssl_verification do
= f.check_box :enable_ssl_verification
%strong Enable SSL verification
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
= f.submit "Add system hook", class: "btn btn-create"
= f.submit 'Add system hook', class: 'btn btn-create'
%hr
- if @hooks.any?
......@@ -62,11 +22,12 @@
- @hooks.each do |hook|
%li
.controls
= link_to 'Test hook', admin_hook_test_path(hook), class: "btn btn-sm"
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
= link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
.monospace= hook.url
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
......@@ -2,10 +2,10 @@
%ul.nav-links
= nav_link(page: dashboard_groups_path) do
= link_to dashboard_groups_path, title: 'Your groups' do
Your Groups
Your groups
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore groups' do
Explore Groups
= link_to explore_groups_path, title: 'Explore public groups' do
Explore public groups
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
......
......@@ -9,7 +9,7 @@
.title-item.author-name
- if todo.author
= link_to_author(todo)
= link_to_author(todo, self_added: todo.self_added?)
- else
(removed)
......@@ -22,6 +22,10 @@
- else
(removed)
- if todo.self_assigned?
.title-item.action-name
to yourself
.title-item
&middot;
......
......@@ -7,6 +7,15 @@
= render 'explore/head'
= render 'nav'
- if cookies[:explore_groups_landing_dismissed] != 'true'
.explore-groups.landing.content-block.js-explore-groups-landing.hidden
%button.dismiss-button{ type: 'button', 'aria-label' => 'Dismiss' }= icon('times')
.svg-container
= custom_icon('icon_explore_groups_splash')
.inner-content
%p Below you will find all the groups that are public.
%p You can easily contribute to them by requesting to join these groups.
- if @groups.present?
= render 'groups'
- else
......
= render "groups/settings_head"
= render 'shared/web_hooks/form', hook: @hook, hooks: @hooks, url_components: [@group]
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
= page_title
%p
#{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be
used for binding events when something is happening within the project.
.col-lg-9.append-bottom-default
= form_for @hook, as: :hook, url: polymorphic_path([@group, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Add webhook', class: 'btn btn-create'
%hr
%h5.prepend-top-default
Webhooks (#{@hooks.count})
- if @hooks.any?
%ul.well-list
- @hooks.each do |hook|
= render 'project_hook', hook: hook
- else
%p.settings-message.text-center.append-bottom-0
No webhooks found, add one in the form above.
......@@ -34,8 +34,7 @@
.row
.col-md-8
.documentation-index
= preserve do
= markdown(@help_index)
= markdown(@help_index)
.col-md-4
.panel.panel-default
.panel-heading
......
......@@ -2,8 +2,7 @@
%div{ class: container_class }
.wiki-holder.prepend-top-default.append-bottom-default
.wiki
= preserve do
= render_wiki_content(@wiki_home)
= render_wiki_content(@wiki_home)
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)
.project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] }
......
......@@ -15,8 +15,8 @@
= render 'projects/blob/viewer_switcher', blob: blob unless blame
.btn-group{ role: "group" }<
= copy_blob_source_button(blob) if !blame && blob.rendered_as_text?(ignore_errors: false)
= open_raw_file_button(namespace_project_raw_path(@project.namespace, @project, @id))
= copy_blob_source_button(blob) unless blame
= open_raw_blob_button(blob)
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
.btn-group{ role: "group" }<
......
- blob = viewer.blob
- rendered_markup = blob.rendered_markup if blob.respond_to?(:rendered_markup)
.file-content.wiki
= markup(blob.name, blob.data)
= markup(blob.name, blob.data, rendered: rendered_markup)
.file-content.video
%video{ src: blob_raw_url, controls: true, data: { setup: '{}' } }
= render 'shared/web_hooks/form', hook: @hook, hooks: @hooks, url_components: [@project.namespace.becomes(Namespace), @project]
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
= page_title
%p
#{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be
used for binding events when something is happening within the project.
.col-lg-9.append-bottom-default
= form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Add webhook', class: 'btn btn-create'
%hr
%h5.prepend-top-default
Webhooks (#{@hooks.count})
- if @hooks.any?
%ul.well-list
- @hooks.each do |hook|
= render 'project_hook', hook: hook
- else
%p.settings-message.text-center.append-bottom-0
No webhooks found, add one in the form above.
= render 'projects/settings/head'
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
= page_title
%p
#{link_to 'Webhooks', help_page_path('user/project/integrations/webhooks')} can be
used for binding events when something is happening within the project.
.col-lg-9.append-bottom-default
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hook_path do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Save changes', class: 'btn btn-create'
......@@ -58,8 +58,7 @@
- if @issue.description.present?
.description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' }
.wiki
= preserve do
= markdown_field(@issue, :description)
= markdown_field(@issue, :description)
%textarea.hidden.js-task-list-field
= @issue.description
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago')
......
......@@ -38,7 +38,7 @@
.panel-heading
Target branch
.panel-body.clearfix
- projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
- projects = target_projects(@project)
.merge-request-select.dropdown
= f.hidden_field :target_project_id
= dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
......
......@@ -6,8 +6,7 @@
- if @merge_request.description.present?
.description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' }
.wiki
= preserve do
= markdown_field(@merge_request, :description)
= markdown_field(@merge_request, :description)
%textarea.hidden.js-task-list-field
= @merge_request.description
......
......@@ -43,8 +43,7 @@
- if @milestone.description.present?
.description
.wiki
= preserve do
= markdown_field(@milestone, :description)
= markdown_field(@milestone, :description)
= render 'shared/milestones/burndown', milestone: @milestone, project: @project, burndown: @burndown
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment