Commit 5aae80e1 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'ce-com/master' into ce-to-ee

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents d28f0594 9f908cfc
...@@ -284,7 +284,7 @@ bundler:audit: ...@@ -284,7 +284,7 @@ bundler:audit:
- master@gitlab/gitlabhq - master@gitlab/gitlabhq
- master@gitlab/gitlab-ee - master@gitlab/gitlab-ee
script: script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941 CVE-2016-6316 CVE-2016-6317" - "bundle exec bundle-audit check --update"
migration paths: migration paths:
stage: test stage: test
...@@ -405,6 +405,7 @@ pages: ...@@ -405,6 +405,7 @@ pages:
- public - public
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
# Insurance in case a gem needed by one of our releases gets yanked from # Insurance in case a gem needed by one of our releases gets yanked from
# rubygems.org in the future. # rubygems.org in the future.
......
...@@ -355,7 +355,7 @@ gem 'oauth2', '~> 1.2.0' ...@@ -355,7 +355,7 @@ gem 'oauth2', '~> 1.2.0'
gem 'paranoia', '~> 2.2' gem 'paranoia', '~> 2.2'
# Health check # Health check
gem 'health_check', '~> 2.2.0' gem 'health_check', '~> 2.6.0'
# System information # System information
gem 'vmstat', '~> 2.3.0' gem 'vmstat', '~> 2.3.0'
......
...@@ -371,7 +371,7 @@ GEM ...@@ -371,7 +371,7 @@ GEM
thor thor
tilt tilt
hashie (3.5.5) hashie (3.5.5)
health_check (2.2.1) health_check (2.6.0)
rails (>= 4.0) rails (>= 4.0)
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
...@@ -700,7 +700,7 @@ GEM ...@@ -700,7 +700,7 @@ GEM
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.0) rubyzip (1.2.1)
rufus-scheduler (3.1.10) rufus-scheduler (3.1.10)
rugged (0.24.0) rugged (0.24.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
...@@ -939,7 +939,7 @@ DEPENDENCIES ...@@ -939,7 +939,7 @@ DEPENDENCIES
gssapi gssapi
haml_lint (~> 0.21.0) haml_lint (~> 0.21.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
health_check (~> 2.2.0) health_check (~> 2.6.0)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
html2text html2text
...@@ -1059,4 +1059,4 @@ DEPENDENCIES ...@@ -1059,4 +1059,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.14.4 1.14.5
...@@ -39,15 +39,10 @@ const PipelineStore = require('./pipelines_store'); ...@@ -39,15 +39,10 @@ const PipelineStore = require('./pipelines_store');
*/ */
data() { data() {
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset; const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
const svgsData = document.querySelector('.pipeline-svgs').dataset;
const store = new PipelineStore(); const store = new PipelineStore();
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = gl.utils.DOMStringMapToObject(svgsData);
return { return {
endpoint: pipelinesTableData.endpoint, endpoint: pipelinesTableData.endpoint,
svgs: svgsObject,
store, store,
state: store.state, state: store.state,
isLoading: false, isLoading: false,
...@@ -101,10 +96,7 @@ const PipelineStore = require('./pipelines_store'); ...@@ -101,10 +96,7 @@ const PipelineStore = require('./pipelines_store');
<div class="table-holder pipelines" <div class="table-holder pipelines"
v-if="!isLoading && state.pipelines.length > 0"> v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component <pipelines-table-component :pipelines="state.pipelines"/>
:pipelines="state.pipelines"
:svgs="svgs">
</pipelines-table-component>
</div> </div>
</div> </div>
`, `,
......
...@@ -9,6 +9,9 @@ import 'vendor/jquery.scrollTo'; ...@@ -9,6 +9,9 @@ import 'vendor/jquery.scrollTo';
import 'vendor/jquery.nicescroll'; import 'vendor/jquery.nicescroll';
import 'vendor/jquery.waitforimages'; import 'vendor/jquery.waitforimages';
import 'select2/select2'; import 'select2/select2';
<<<<<<< HEAD
// EE-only // EE-only
import 'vendor/jquery.tablesorter'; import 'vendor/jquery.tablesorter';
=======
>>>>>>> ce-com/master
...@@ -25,6 +25,9 @@ require('./lib/utils/common_utils'); ...@@ -25,6 +25,9 @@ require('./lib/utils/common_utils');
}, },
}, },
ReferenceFilter: { ReferenceFilter: {
'.tooltip'(el, text) {
return '';
},
'a.gfm:not([data-link=true])'(el, text) { 'a.gfm:not([data-link=true])'(el, text) {
return el.dataset.original || text; return el.dataset.original || text;
}, },
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/* global Vue */ import Vue from 'vue';
import iconCommit from '../svg/icon_commit.svg';
((global) => { ((global) => {
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
...@@ -9,6 +10,11 @@ ...@@ -9,6 +10,11 @@
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() {
return { iconCommit };
},
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
...@@ -31,7 +37,7 @@ ...@@ -31,7 +37,7 @@
</h5> </h5>
<span> <span>
First First
<span class="commit-icon">${global.cycleAnalytics.svgs.iconCommit}</span> <span class="commit-icon">${iconCommit}</span>
<a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a> <a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
pushed by pushed by
<a :href="commit.author.webUrl" class="commit-author-link"> <a :href="commit.author.webUrl" class="commit-author-link">
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/* global Vue */ import Vue from 'vue';
import iconBranch from '../svg/icon_branch.svg';
((global) => { ((global) => {
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
...@@ -9,6 +10,9 @@ ...@@ -9,6 +10,9 @@
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() {
return { iconBranch };
},
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
...@@ -22,7 +26,7 @@ ...@@ -22,7 +26,7 @@
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a> <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i> <i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a> <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span> <span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a> <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5> </h5>
<span> <span>
......
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
/* global Vue */ import Vue from 'vue';
import iconBuildStatus from '../svg/icon_build_status.svg';
import iconBranch from '../svg/icon_branch.svg';
((global) => { ((global) => {
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
...@@ -9,6 +11,9 @@ ...@@ -9,6 +11,9 @@
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() {
return { iconBuildStatus, iconBranch };
},
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
...@@ -18,13 +23,13 @@ ...@@ -18,13 +23,13 @@
<li v-for="build in items" class="stage-event-item item-build-component"> <li v-for="build in items" class="stage-event-item item-build-component">
<div class="item-details"> <div class="item-details">
<h5 class="item-title"> <h5 class="item-title">
<span class="icon-build-status">${global.cycleAnalytics.svgs.iconBuildStatus}</span> <span class="icon-build-status">${iconBuildStatus}</span>
<a :href="build.url" class="item-build-name">{{ build.name }}</a> <a :href="build.url" class="item-build-name">{{ build.name }}</a>
&middot; &middot;
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a> <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i> <i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a> <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span> <span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a> <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5> </h5>
<span> <span>
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
window.Vue = require('vue'); window.Vue = require('vue');
window.Cookies = require('js-cookie'); window.Cookies = require('js-cookie');
require('./svg/icon_branch');
require('./svg/icon_build_status');
require('./svg/icon_commit');
require('./components/stage_code_component'); require('./components/stage_code_component');
require('./components/stage_issue_component'); require('./components/stage_issue_component');
require('./components/stage_plan_component'); require('./components/stage_plan_component');
......
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><path fill="#8C8C8C" fill-rule="evenodd" d="M9.678 6.722C9.353 5.167 8.053 4 6.5 4S3.647 5.167 3.322 6.722h-2.6c-.397 0-.722.35-.722.778 0 .428.325.778.722.778h2.6C3.647 9.833 4.947 11 6.5 11s2.853-1.167 3.178-2.722h2.6c.397 0 .722-.35.722-.778 0-.428-.325-.778-.722-.778h-2.6zM4.694 7.5c0-1.09.795-1.944 1.806-1.944 1.01 0 1.806.855 1.806 1.944 0 1.09-.795 1.944-1.806 1.944-1.01 0-1.806-.855-1.806-1.944z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><g fill="#31AF64" fill-rule="evenodd"><path d="M12.5 7c0-3.038-2.462-5.5-5.5-5.5S1.5 3.962 1.5 7s2.462 5.5 5.5 5.5 5.5-2.462 5.5-5.5zM0 7c0-3.866 3.134-7 7-7s7 3.134 7 7-3.134 7-7 7-7-3.134-7-7z"/><path d="M6.28 7.697L5.045 6.464c-.117-.117-.305-.117-.42-.002l-.614.614c-.11.113-.11.303.007.42l1.91 1.91c.19.19.51.197.703.004l.264-.265L9.997 6.04c.108-.107.107-.293-.01-.408l-.612-.614c-.114-.113-.298-.12-.41-.01L6.28 7.7z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><path fill="#8F8F8F" fill-rule="evenodd" d="M28.777 18c-.91-4.008-4.494-7-8.777-7-4.283 0-7.868 2.992-8.777 7H4.01C2.9 18 2 18.895 2 20c0 1.112.9 2 2.01 2h7.213c.91 4.008 4.494 7 8.777 7 4.283 0 7.868-2.992 8.777-7h7.214C37.1 22 38 21.105 38 20c0-1.112-.9-2-2.01-2h-7.213zM20 25c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5z"/></svg>
...@@ -25,6 +25,10 @@ require('./lib/utils/url_utility'); ...@@ -25,6 +25,10 @@ require('./lib/utils/url_utility');
isBound = true; isBound = true;
} }
if (gl.utils.getLocationHash()) {
this.highlightSelectedLine();
}
this.openAnchoredDiff(); this.openAnchoredDiff();
} }
...@@ -78,7 +82,7 @@ require('./lib/utils/url_utility'); ...@@ -78,7 +82,7 @@ require('./lib/utils/url_utility');
if (nothingHereBlock.length) { if (nothingHereBlock.length) {
const clickTarget = $('.js-file-title, .click-to-expand', diffFile); const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => { diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlighSelectedLine(); this.highlightSelectedLine();
if (cb) cb(); if (cb) cb();
}); });
} else if (cb) { } else if (cb) {
...@@ -94,7 +98,7 @@ require('./lib/utils/url_utility'); ...@@ -94,7 +98,7 @@ require('./lib/utils/url_utility');
} else { } else {
window.location.hash = hash; window.location.hash = hash;
} }
this.highlighSelectedLine(); this.highlightSelectedLine();
} }
diffViewType() { diffViewType() {
...@@ -108,7 +112,7 @@ require('./lib/utils/url_utility'); ...@@ -108,7 +112,7 @@ require('./lib/utils/url_utility');
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10)); return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
} }
highlighSelectedLine() { highlightSelectedLine() {
const hash = gl.utils.getLocationHash(); const hash = gl.utils.getLocationHash();
const $diffFiles = $('.diff-file'); const $diffFiles = $('.diff-file');
$diffFiles.find('.hll').removeClass('hll'); $diffFiles.find('.hll').removeClass('hll');
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
/* global WeightSelect */ /* global WeightSelect */
/* global AdminEmailSelect */ /* global AdminEmailSelect */
import GroupsList from './groups_list';
import ProjectsList from './projects_list';
const ShortcutsBlob = require('./shortcuts_blob'); const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout'); const UserCallout = require('./user_callout');
...@@ -98,6 +101,18 @@ const UserCallout = require('./user_callout'); ...@@ -98,6 +101,18 @@ const UserCallout = require('./user_callout');
case 'dashboard:todos:index': case 'dashboard:todos:index':
new gl.Todos(); new gl.Todos();
break; break;
case 'dashboard:projects:index':
case 'dashboard:projects:starred':
case 'explore:projects:index':
case 'explore:projects:trending':
case 'explore:projects:starred':
case 'admin:projects:index':
new ProjectsList();
break;
case 'dashboard:groups:index':
case 'explore:groups:index':
new GroupsList();
break;
case 'projects:milestones:new': case 'projects:milestones:new':
case 'projects:milestones:edit': case 'projects:milestones:edit':
case 'projects:milestones:update': case 'projects:milestones:update':
...@@ -160,9 +175,6 @@ const UserCallout = require('./user_callout'); ...@@ -160,9 +175,6 @@ const UserCallout = require('./user_callout');
case 'dashboard:activity': case 'dashboard:activity':
new gl.Activities(); new gl.Activities();
break; break;
case 'dashboard:projects:starred':
new gl.Activities();
break;
case 'projects:commit:show': case 'projects:commit:show':
new Commit(); new Commit();
new gl.Diff(); new gl.Diff();
...@@ -205,6 +217,7 @@ const UserCallout = require('./user_callout'); ...@@ -205,6 +217,7 @@ const UserCallout = require('./user_callout');
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new NotificationsForm(); new NotificationsForm();
new NotificationsDropdown(); new NotificationsDropdown();
new ProjectsList();
break; break;
case 'groups:group_members:index': case 'groups:group_members:index':
new gl.MemberExpirationDate(); new gl.MemberExpirationDate();
......
...@@ -36,9 +36,6 @@ module.exports = Vue.component('environment-component', { ...@@ -36,9 +36,6 @@ module.exports = Vue.component('environment-component', {
projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
newEnvironmentPath: environmentsData.newEnvironmentPath, newEnvironmentPath: environmentsData.newEnvironmentPath,
helpPagePath: environmentsData.helpPagePath, helpPagePath: environmentsData.helpPagePath,
commitIconSvg: environmentsData.commitIconSvg,
playIconSvg: environmentsData.playIconSvg,
terminalIconSvg: environmentsData.terminalIconSvg,
// Pagination Properties, // Pagination Properties,
paginationInformation: {}, paginationInformation: {},
...@@ -89,7 +86,11 @@ module.exports = Vue.component('environment-component', { ...@@ -89,7 +86,11 @@ module.exports = Vue.component('environment-component', {
this.isLoading = true; this.isLoading = true;
<<<<<<< HEAD:app/assets/javascripts/environments/components/environment.js
return this.service.get() return this.service.get()
=======
return service.get()
>>>>>>> ce-com/master:app/assets/javascripts/environments/components/environment.js
.then(resp => ({ .then(resp => ({
headers: resp.headers, headers: resp.headers,
body: resp.json(), body: resp.json(),
...@@ -194,6 +195,7 @@ module.exports = Vue.component('environment-component', { ...@@ -194,6 +195,7 @@ module.exports = Vue.component('environment-component', {
<environment-table <environment-table
:environments="state.environments" :environments="state.environments"
:can-create-deployment="canCreateDeploymentParsed" :can-create-deployment="canCreateDeploymentParsed"
<<<<<<< HEAD:app/assets/javascripts/environments/components/environment.js
:can-read-environment="canReadEnvironmentParsed" :can-read-environment="canReadEnvironmentParsed"
:play-icon-svg="playIconSvg" :play-icon-svg="playIconSvg"
:terminal-icon-svg="terminalIconSvg" :terminal-icon-svg="terminalIconSvg"
...@@ -202,6 +204,9 @@ module.exports = Vue.component('environment-component', { ...@@ -202,6 +204,9 @@ module.exports = Vue.component('environment-component', {
:store="store" :store="store"
:service="service"> :service="service">
</environment-table> </environment-table>
=======
:can-read-environment="canReadEnvironmentParsed"/>
>>>>>>> ce-com/master:app/assets/javascripts/environments/components/environment.js
</div> </div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1" <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
......
const Vue = require('vue'); const Vue = require('vue');
const playIconSvg = require('icons/_icon_play.svg');
module.exports = Vue.component('actions-component', { module.exports = Vue.component('actions-component', {
props: { props: {
...@@ -7,11 +8,10 @@ module.exports = Vue.component('actions-component', { ...@@ -7,11 +8,10 @@ module.exports = Vue.component('actions-component', {
required: false, required: false,
default: () => [], default: () => [],
}, },
playIconSvg: {
type: String,
required: false,
}, },
data() {
return { playIconSvg };
}, },
template: ` template: `
...@@ -28,9 +28,7 @@ module.exports = Vue.component('actions-component', { ...@@ -28,9 +28,7 @@ module.exports = Vue.component('actions-component', {
data-method="post" data-method="post"
rel="nofollow" rel="nofollow"
class="js-manual-action-link"> class="js-manual-action-link">
${playIconSvg}
<span class="js-action-play-icon-container" v-html="playIconSvg"></span>
<span> <span>
{{action.name}} {{action.name}}
</span> </span>
......
...@@ -46,6 +46,7 @@ module.exports = Vue.component('environment-item', { ...@@ -46,6 +46,7 @@ module.exports = Vue.component('environment-item', {
required: false, required: false,
default: false, default: false,
}, },
<<<<<<< HEAD:app/assets/javascripts/environments/components/environment_item.js
commitIconSvg: { commitIconSvg: {
type: String, type: String,
...@@ -66,6 +67,8 @@ module.exports = Vue.component('environment-item', { ...@@ -66,6 +67,8 @@ module.exports = Vue.component('environment-item', {
type: Function, type: Function,
required: false, required: false,
}, },
=======
>>>>>>> ce-com/master:app/assets/javascripts/environments/components/environment_item.js
}, },
computed: { computed: {
...@@ -507,9 +510,7 @@ module.exports = Vue.component('environment-item', { ...@@ -507,9 +510,7 @@ module.exports = Vue.component('environment-item', {
:commit-url="commitUrl" :commit-url="commitUrl"
:short-sha="commitShortSha" :short-sha="commitShortSha"
:title="commitTitle" :title="commitTitle"
:author="commitAuthor" :author="commitAuthor"/>
:commit-icon-svg="commitIconSvg">
</commit-component>
</div> </div>
<p v-if="!model.isFolder && !hasLastDeploymentKey" class="commit-title"> <p v-if="!model.isFolder && !hasLastDeploymentKey" class="commit-title">
No deployments yet No deployments yet
...@@ -526,27 +527,20 @@ module.exports = Vue.component('environment-item', { ...@@ -526,27 +527,20 @@ module.exports = Vue.component('environment-item', {
<td class="environments-actions"> <td class="environments-actions">
<div v-if="!model.isFolder" class="btn-group pull-right" role="group"> <div v-if="!model.isFolder" class="btn-group pull-right" role="group">
<actions-component v-if="hasManualActions && canCreateDeployment" <actions-component v-if="hasManualActions && canCreateDeployment"
:play-icon-svg="playIconSvg" :actions="manualActions"/>
:actions="manualActions">
</actions-component>
<external-url-component v-if="externalURL && canReadEnvironment" <external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL"> :external-url="externalURL"/>
</external-url-component>
<stop-component v-if="hasStopAction && canCreateDeployment" <stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path"> :stop-url="model.stop_path"/>
</stop-component>
<terminal-button-component v-if="model && model.terminal_path" <terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg" :terminal-path="model.terminal_path"/>
:terminal-path="model.terminal_path">
</terminal-button-component>
<rollback-component v-if="canRetry && canCreateDeployment" <rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment" :is-last-deployment="isLastDeployment"
:retry-url="retryUrl"> :retry-url="retryUrl"/>
</rollback-component>
</div> </div>
</td> </td>
</tr> </tr>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Used in environments table. * Used in environments table.
*/ */
const Vue = require('vue'); const Vue = require('vue');
const terminalIconSvg = require('icons/_icon_terminal.svg');
module.exports = Vue.component('terminal-button-component', { module.exports = Vue.component('terminal-button-component', {
props: { props: {
...@@ -10,16 +11,16 @@ module.exports = Vue.component('terminal-button-component', { ...@@ -10,16 +11,16 @@ module.exports = Vue.component('terminal-button-component', {
type: String, type: String,
default: '', default: '',
}, },
terminalIconSvg: {
type: String,
default: '',
}, },
data() {
return { terminalIconSvg };
}, },
template: ` template: `
<a class="btn terminal-button" <a class="btn terminal-button"
:href="terminalPath"> :href="terminalPath">
<span class="js-terminal-icon-container" v-html="terminalIconSvg"></span> ${terminalIconSvg}
</a> </a>
`, `,
}); });
...@@ -33,6 +33,7 @@ module.exports = Vue.component('environment-table-component', { ...@@ -33,6 +33,7 @@ module.exports = Vue.component('environment-table-component', {
required: false, required: false,
default: false, default: false,
}, },
<<<<<<< HEAD:app/assets/javascripts/environments/components/environments_table.js
commitIconSvg: { commitIconSvg: {
type: String, type: String,
...@@ -66,6 +67,8 @@ module.exports = Vue.component('environment-table-component', { ...@@ -66,6 +67,8 @@ module.exports = Vue.component('environment-table-component', {
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
=======
>>>>>>> ce-com/master:app/assets/javascripts/environments/components/environments_table.js
}, },
template: ` template: `
...@@ -87,6 +90,7 @@ module.exports = Vue.component('environment-table-component', { ...@@ -87,6 +90,7 @@ module.exports = Vue.component('environment-table-component', {
<tr is="environment-item" <tr is="environment-item"
:model="model" :model="model"
:can-create-deployment="canCreateDeployment" :can-create-deployment="canCreateDeployment"
<<<<<<< HEAD:app/assets/javascripts/environments/components/environments_table.js
:can-read-environment="canReadEnvironment" :can-read-environment="canReadEnvironment"
:play-icon-svg="playIconSvg" :play-icon-svg="playIconSvg"
:terminal-icon-svg="terminalIconSvg" :terminal-icon-svg="terminalIconSvg"
...@@ -104,6 +108,9 @@ module.exports = Vue.component('environment-table-component', { ...@@ -104,6 +108,9 @@ module.exports = Vue.component('environment-table-component', {
</deploy-board> </deploy-board>
</td> </td>
</tr> </tr>
=======
:can-read-environment="canReadEnvironment"></tr>
>>>>>>> ce-com/master:app/assets/javascripts/environments/components/environments_table.js
</template> </template>
</tbody> </tbody>
</table> </table>
......
...@@ -93,7 +93,11 @@ module.exports = Vue.component('environment-folder-view', { ...@@ -93,7 +93,11 @@ module.exports = Vue.component('environment-folder-view', {
this.isLoading = true; this.isLoading = true;
<<<<<<< HEAD:app/assets/javascripts/environments/folder/environments_folder_view.js
return this.service.get() return this.service.get()
=======
return service.get()
>>>>>>> ce-com/master:app/assets/javascripts/environments/folder/environments_folder_view.js
.then(resp => ({ .then(resp => ({
headers: resp.headers, headers: resp.headers,
body: resp.json(), body: resp.json(),
......
...@@ -30,14 +30,6 @@ class EnvironmentsStore { ...@@ -30,14 +30,6 @@ class EnvironmentsStore {
* If the `size` is bigger than 1, it means it should be rendered as a folder. * If the `size` is bigger than 1, it means it should be rendered as a folder.
* In those cases we add `isFolder` key in order to render it properly. * In those cases we add `isFolder` key in order to render it properly.
* *
* Top level environments - when the size is 1 - with `rollout_status_path`
* can render a deploy board. We add `isDeployBoardVisible` and `deployBoardData`
* keys to those environments.
* The first key will let's us know if we should or not render the deploy board.
* It will be toggled when the user clicks to seee the deploy board.
*
* The second key will allow us to update the environment with the received deploy board data.
*
* @param {Array} environments * @param {Array} environments
* @returns {Array} * @returns {Array}
*/ */
...@@ -45,21 +37,15 @@ class EnvironmentsStore { ...@@ -45,21 +37,15 @@ class EnvironmentsStore {
const filteredEnvironments = environments.map((env) => { const filteredEnvironments = environments.map((env) => {
let filtered = {}; let filtered = {};
if (env.size > 1) {
filtered = Object.assign({}, env, { isFolder: true, folderName: env.name });
}
if (env.latest) { if (env.latest) {
filtered = Object.assign({}, env, env.latest); filtered = Object.assign(filtered, env, env.latest);
delete filtered.latest; delete filtered.latest;
} else { } else {
filtered = Object.assign({}, env); filtered = Object.assign(filtered, env);
}
if (filtered.size > 1) {
filtered = Object.assign(filtered, env, { isFolder: true, folderName: env.name });
} else if (filtered.size === 1 && filtered.rollout_status_path) {
filtered = Object.assign(filtered, env, {
hasDeployBoard: true,
isDeployBoardVisible: false,
deployBoardData: {},
});
} }
return filtered; return filtered;
...@@ -70,20 +56,6 @@ class EnvironmentsStore { ...@@ -70,20 +56,6 @@ class EnvironmentsStore {
return filteredEnvironments; return filteredEnvironments;
} }
/**
* Stores the pagination information needed to render the pagination for the
* table.
*
* Normalizes the headers to uppercase since they can be provided either
* in uppercase or lowercase.
*
* Parses to an integer the normalized ones needed for the pagination component.
*
* Stores the normalized and parsed information.
*
* @param {Object} pagination = {}
* @return {Object}
*/
setPagination(pagination = {}) { setPagination(pagination = {}) {
const normalizedHeaders = gl.utils.normalizeHeaders(pagination); const normalizedHeaders = gl.utils.normalizeHeaders(pagination);
const paginationInformation = gl.utils.parseIntPagination(normalizedHeaders); const paginationInformation = gl.utils.parseIntPagination(normalizedHeaders);
...@@ -113,48 +85,6 @@ class EnvironmentsStore { ...@@ -113,48 +85,6 @@ class EnvironmentsStore {
this.state.stoppedCounter = count; this.state.stoppedCounter = count;
return count; return count;
} }
/**
* Toggles deploy board visibility for the provided environment ID.
*
* @param {Object} environment
* @return {Array}
*/
toggleDeployBoard(environmentID) {
const environments = this.state.environments.slice();
this.state.environments = environments.map((env) => {
let updated = Object.assign({}, env);
if (env.id === environmentID) {
updated = Object.assign({}, updated, { isDeployBoardVisible: !env.isDeployBoardVisible });
}
return updated;
});
return this.state.environments;
}
/**
* Store deploy board data for given environment.
*
* @param {Number} environmentID
* @param {Object} deployBoard
* @return {Array}
*/
storeDeployBoard(environmentID, deployBoard) {
const environments = Object.assign([], this.state.environments);
this.state.environments = environments.map((env) => {
let updated = Object.assign({}, env);
if (env.id === environmentID) {
updated = Object.assign({}, updated, { deployBoardData: deployBoard });
}
return updated;
});
return this.state.environments;
}
} }
module.exports = EnvironmentsStore; module.exports = EnvironmentsStore;
/**
* Makes search request for content when user types a value in the search input.
* Updates the html content of the page with the received one.
*/
export default class FilterableList {
constructor(form, filter, holder) {
this.filterForm = form;
this.listFilterElement = filter;
this.listHolderElement = holder;
}
initSearch() {
this.debounceFilter = _.debounce(this.filterResults.bind(this), 500);
this.listFilterElement.removeEventListener('input', this.debounceFilter);
this.listFilterElement.addEventListener('input', this.debounceFilter);
}
filterResults() {
const form = this.filterForm;
const filterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`;
$(this.listHolderElement).fadeTo(250, 0.5);
return $.ajax({
url: form.getAttribute('action'),
data: $(form).serialize(),
type: 'GET',
dataType: 'json',
context: this,
complete() {
$(this.listHolderElement).fadeTo(250, 1);
},
success(data) {
this.listHolderElement.innerHTML = data.html;
// Change url so if user reload a page - search results are saved
return window.history.replaceState({
page: filterUrl,
}, document.title, filterUrl);
},
});
}
}
import FilterableList from './filterable_list';
/**
* Makes search request for groups when user types a value in the search input.
* Updates the html content of the page with the received one.
*/
export default class GroupsList {
constructor() {
const form = document.querySelector('form#group-filter-form');
const filter = document.querySelector('.js-groups-list-filter');
const holder = document.querySelector('.js-groups-list-holder');
if (form && filter && holder) {
const list = new FilterableList(form, filter, holder);
list.initSearch();
}
}
}
/* global Vue */ /* global Vue */
import stopwatchSvg from 'icons/_icon_stopwatch.svg';
require('../../../lib/utils/pretty_time'); require('../../../lib/utils/pretty_time');
(() => { (() => {
...@@ -11,7 +13,6 @@ require('../../../lib/utils/pretty_time'); ...@@ -11,7 +13,6 @@ require('../../../lib/utils/pretty_time');
'showNoTimeTrackingState', 'showNoTimeTrackingState',
'timeSpentHumanReadable', 'timeSpentHumanReadable',
'timeEstimateHumanReadable', 'timeEstimateHumanReadable',
'stopwatchSvg',
], ],
methods: { methods: {
abbreviateTime(timeStr) { abbreviateTime(timeStr) {
...@@ -20,7 +21,7 @@ require('../../../lib/utils/pretty_time'); ...@@ -20,7 +21,7 @@ require('../../../lib/utils/pretty_time');
}, },
template: ` template: `
<div class='sidebar-collapsed-icon'> <div class='sidebar-collapsed-icon'>
<div v-html='stopwatchSvg'></div> ${stopwatchSvg}
<div class='time-tracking-collapsed-summary'> <div class='time-tracking-collapsed-summary'>
<div class='compare' v-if='showComparisonState'> <div class='compare' v-if='showComparisonState'>
<span>{{ abbreviateTime(timeSpentHumanReadable) }} / {{ abbreviateTime(timeEstimateHumanReadable) }}</span> <span>{{ abbreviateTime(timeSpentHumanReadable) }} / {{ abbreviateTime(timeEstimateHumanReadable) }}</span>
......
...@@ -15,7 +15,6 @@ require('./comparison_pane'); ...@@ -15,7 +15,6 @@ require('./comparison_pane');
'time_spent', 'time_spent',
'human_time_estimate', 'human_time_estimate',
'human_time_spent', 'human_time_spent',
'stopwatchSvg',
'docsUrl', 'docsUrl',
], ],
data() { data() {
...@@ -71,8 +70,7 @@ require('./comparison_pane'); ...@@ -71,8 +70,7 @@ require('./comparison_pane');
:show-spent-only-state='showSpentOnlyState' :show-spent-only-state='showSpentOnlyState'
:show-estimate-only-state='showEstimateOnlyState' :show-estimate-only-state='showEstimateOnlyState'
:time-spent-human-readable='timeSpentHumanReadable' :time-spent-human-readable='timeSpentHumanReadable'
:time-estimate-human-readable='timeEstimateHumanReadable' :time-estimate-human-readable='timeEstimateHumanReadable'>
:stopwatch-svg='stopwatchSvg'>
</time-tracking-collapsed-state> </time-tracking-collapsed-state>
<div class='title hide-collapsed'> <div class='title hide-collapsed'>
Time tracking Time tracking
......
/* eslint-disable func-names, space-before-function-paren */
/*= require cropper */
(function() {
}).call(window);
...@@ -246,17 +246,6 @@ ...@@ -246,17 +246,6 @@
previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10), previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10),
}); });
/**
* Transforms a DOMStringMap into a plain object.
*
* @param {DOMStringMap} DOMStringMapObject
* @returns {Object}
*/
w.gl.utils.DOMStringMapToObject = DOMStringMapObject => Object.keys(DOMStringMapObject).reduce((acc, element) => {
acc[element] = DOMStringMapObject[element];
return acc;
}, {});
/** /**
* Updates the search parameter of a URL given the parameter and values provided. * Updates the search parameter of a URL given the parameter and values provided.
* *
......
...@@ -65,9 +65,10 @@ require('vendor/latinise'); ...@@ -65,9 +65,10 @@ require('vendor/latinise');
} }
}; };
gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) { gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine; var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
removedLastNewLine = false; removedLastNewLine = false;
removedFirstNewLine = false; removedFirstNewLine = false;
currentLineEmpty = false;
// Remove the first newline // Remove the first newline
if (selected.indexOf('\n') === 0) { if (selected.indexOf('\n') === 0) {
...@@ -82,7 +83,17 @@ require('vendor/latinise'); ...@@ -82,7 +83,17 @@ require('vendor/latinise');
} }
selectedSplit = selected.split('\n'); selectedSplit = selected.split('\n');
startChar = !wrap && textArea.selectionStart > 0 ? '\n' : '';
if (!wrap) {
lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n');
// Check whether the current line is empty or consists only of spaces(=handle as empty)
if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) {
currentLineEmpty = true;
}
}
startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) { if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
if (blockTag != null) { if (blockTag != null) {
...@@ -142,9 +153,8 @@ require('vendor/latinise'); ...@@ -142,9 +153,8 @@ require('vendor/latinise');
} }
}; };
gl.text.updateText = function(textArea, tag, blockTag, wrap) { gl.text.updateText = function(textArea, tag, blockTag, wrap) {
var $textArea, oldVal, selected, text; var $textArea, selected, text;
$textArea = $(textArea); $textArea = $(textArea);
oldVal = $textArea.val();
textArea = $textArea.get(0); textArea = $textArea.get(0);
text = $textArea.val(); text = $textArea.val();
selected = this.selectedText(text, textArea); selected = this.selectedText(text, textArea);
......
...@@ -204,6 +204,7 @@ require('./visibility_select'); ...@@ -204,6 +204,7 @@ require('./visibility_select');
require('./wikis'); require('./wikis');
require('./zen_mode'); require('./zen_mode');
<<<<<<< HEAD:app/assets/javascripts/main.js
// EE-only scripts // EE-only scripts
require('./geo/geo_bundle'); require('./geo/geo_bundle');
require('./admin_email_select'); require('./admin_email_select');
...@@ -213,6 +214,8 @@ require('./ldap_groups_select'); ...@@ -213,6 +214,8 @@ require('./ldap_groups_select');
require('./path_locks'); require('./path_locks');
require('./weight_select'); require('./weight_select');
=======
>>>>>>> ce-com/master:app/assets/javascripts/main.js
(function () { (function () {
document.addEventListener('beforeunload', function () { document.addEventListener('beforeunload', function () {
// Unbind scroll events // Unbind scroll events
......
/* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */ /* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
import 'vendor/cropper';
((global) => { ((global) => {
// Matches everything but the file name // Matches everything but the file name
const FILENAMEREGEX = /^.*[\\\/]/; const FILENAMEREGEX = /^.*[\\\/]/;
......
/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, max-len */ import FilterableList from './filterable_list';
(function() { /**
window.ProjectsList = { * Makes search request for projects when user types a value in the search input.
init: function() { * Updates the html content of the page with the received one.
$(".projects-list-filter").off('keyup'); */
this.initSearch(); export default class ProjectsList {
return this.initPagination(); constructor() {
}, const form = document.querySelector('form#project-filter-form');
initSearch: function() { const filter = document.querySelector('.js-projects-list-filter');
var debounceFilter, projectsListFilter; const holder = document.querySelector('.js-projects-list-holder');
projectsListFilter = $('.projects-list-filter');
debounceFilter = _.debounce(window.ProjectsList.filterResults, 500); if (form && filter && holder) {
return projectsListFilter.on('keyup', function(e) { const list = new FilterableList(form, filter, holder);
if (projectsListFilter.val() !== '') { list.initSearch();
return debounceFilter();
} }
});
},
filterResults: function() {
var form, project_filter_url, search;
$('.projects-list-holder').fadeTo(250, 0.5);
form = null;
form = $("form#project-filter-form");
search = $(".projects-list-filter").val();
project_filter_url = form.attr('action') + '?' + form.serialize();
return $.ajax({
type: "GET",
url: form.attr('action'),
data: form.serialize(),
complete: function() {
return $('.projects-list-holder').fadeTo(250, 1);
},
success: function(data) {
$('.projects-list-holder').replaceWith(data.html);
return history.replaceState({
page: project_filter_url
// Change url so if user reload a page - search results are saved
}, document.title, project_filter_url);
},
dataType: "json"
});
},
initPagination: function() {
return $('.projects-list-holder .pagination').on('ajax:success', function(e, data) {
return $('.projects-list-holder').replaceWith(data.html);
});
} }
}; }
}).call(window);
/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, guard-for-in, no-restricted-syntax, max-len */ /* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */
/* global ProtectedBranchDropdown */ /* global ProtectedBranchDropdown */
/* global Flash */
(global => { (global => {
global.gl = global.gl || {}; global.gl = global.gl || {};
const ACCESS_LEVELS = {
MERGE: 'merge_access_levels',
PUSH: 'push_access_levels',
};
const LEVEL_TYPES = {
ROLE: 'role',
USER: 'user',
GROUP: 'group'
};
gl.ProtectedBranchCreate = class { gl.ProtectedBranchCreate = class {
constructor() { constructor() {
this.$wrap = this.$form = $('#new_protected_branch'); this.$wrap = this.$form = $('#new_protected_branch');
this.buildDropdowns(); this.buildDropdowns();
this.$branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
this.bindEvents();
}
bindEvents() {
this.$form.on('submit', this.onFormSubmit.bind(this));
} }
buildDropdowns() { buildDropdowns() {
...@@ -36,87 +18,38 @@ ...@@ -36,87 +18,38 @@
this.onSelectCallback = this.onSelect.bind(this); this.onSelectCallback = this.onSelect.bind(this);
// Allowed to Merge dropdown // Allowed to Merge dropdown
this[`${ACCESS_LEVELS.MERGE}_dropdown`] = new gl.ProtectedBranchAccessDropdown({ new gl.ProtectedBranchAccessDropdown({
$dropdown: $allowedToMergeDropdown, $dropdown: $allowedToMergeDropdown,
accessLevelsData: gon.merge_access_levels, data: gon.merge_access_levels,
onSelect: this.onSelectCallback, onSelect: this.onSelectCallback
accessLevel: ACCESS_LEVELS.MERGE
}); });
// Allowed to Push dropdown // Allowed to Push dropdown
this[`${ACCESS_LEVELS.PUSH}_dropdown`] = new gl.ProtectedBranchAccessDropdown({ new gl.ProtectedBranchAccessDropdown({
$dropdown: $allowedToPushDropdown, $dropdown: $allowedToPushDropdown,
accessLevelsData: gon.push_access_levels, data: gon.push_access_levels,
onSelect: this.onSelectCallback, onSelect: this.onSelectCallback
accessLevel: ACCESS_LEVELS.PUSH
}); });
// Select default
$allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
$allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
// Protected branch dropdown // Protected branch dropdown
new window.ProtectedBranchDropdown({ new ProtectedBranchDropdown({
$dropdown: this.$wrap.find('.js-protected-branch-select'), $dropdown: this.$wrap.find('.js-protected-branch-select'),
onSelect: this.onSelectCallback onSelect: this.onSelectCallback
}); });
} }
// Enable submit button after selecting an option // This will run after clicked callback
onSelect() { onSelect() {
const $allowedToMerge = this[`${ACCESS_LEVELS.MERGE}_dropdown`].getSelectedItems(); // Enable submit button
const $allowedToPush = this[`${ACCESS_LEVELS.PUSH}_dropdown`].getSelectedItems(); const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
const toggle = !(this.$wrap.find('input[name="protected_branch[name]"]').val() && $allowedToMerge.length && $allowedToPush.length); const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
this.$form.find('input[type="submit"]').attr('disabled', toggle);
}
getFormData() {
const formData = {
authenticity_token: this.$form.find('input[name="authenticity_token"]').val(),
protected_branch: {
name: this.$wrap.find('input[name="protected_branch[name]"]').val(),
}
};
for (const ACCESS_LEVEL in ACCESS_LEVELS) { this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length));
const selectedItems = this[`${ACCESS_LEVELS[ACCESS_LEVEL]}_dropdown`].getSelectedItems();
const levelAttributes = [];
for (let i = 0; i < selectedItems.length; i += 1) {
const current = selectedItems[i];
if (current.type === LEVEL_TYPES.USER) {
levelAttributes.push({
user_id: selectedItems[i].user_id
});
} else if (current.type === LEVEL_TYPES.ROLE) {
levelAttributes.push({
access_level: selectedItems[i].access_level
});
} else if (current.type === LEVEL_TYPES.GROUP) {
levelAttributes.push({
group_id: selectedItems[i].group_id
});
}
}
formData.protected_branch[`${ACCESS_LEVELS[ACCESS_LEVEL]}_attributes`] = levelAttributes;
}
return formData;
}
onFormSubmit(e) {
e.preventDefault();
$.ajax({
url: this.$form.attr('action'),
method: this.$form.attr('method'),
data: this.getFormData()
})
.success(() => {
location.reload();
})
.fail(() => {
new Flash('Failed to protect the branch');
});
} }
}; };
})(window); })(window);
/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, dot-notation, no-unused-vars, no-restricted-syntax, guard-for-in, max-len */ /* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */
/* global Flash */ /* global Flash */
(global => { (global => {
global.gl = global.gl || {}; global.gl = global.gl || {};
const ACCESS_LEVELS = {
MERGE: 'merge_access_levels',
PUSH: 'push_access_levels',
};
const LEVEL_TYPES = {
ROLE: 'role',
USER: 'user',
GROUP: 'group'
};
gl.ProtectedBranchEdit = class { gl.ProtectedBranchEdit = class {
constructor(options) { constructor(options) {
this.$wraps = {};
this.hasChanges = false;
this.$wrap = options.$wrap; this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.$wraps[ACCESS_LEVELS.MERGE] = this.$allowedToMergeDropdown.closest(`.${ACCESS_LEVELS.MERGE}-container`);
this.$wraps[ACCESS_LEVELS.PUSH] = this.$allowedToPushDropdown.closest(`.${ACCESS_LEVELS.PUSH}-container`);
this.buildDropdowns(); this.buildDropdowns();
} }
buildDropdowns() { buildDropdowns() {
// Allowed to merge dropdown // Allowed to merge dropdown
this['merge_access_levels_dropdown'] = new gl.ProtectedBranchAccessDropdown({ new gl.ProtectedBranchAccessDropdown({
accessLevel: ACCESS_LEVELS.MERGE,
accessLevelsData: gon.merge_access_levels,
$dropdown: this.$allowedToMergeDropdown, $dropdown: this.$allowedToMergeDropdown,
onSelect: this.onSelectOption.bind(this), data: gon.merge_access_levels,
onHide: this.onDropdownHide.bind(this) onSelect: this.onSelect.bind(this)
}); });
// Allowed to push dropdown // Allowed to push dropdown
this['push_access_levels_dropdown'] = new gl.ProtectedBranchAccessDropdown({ new gl.ProtectedBranchAccessDropdown({
accessLevel: ACCESS_LEVELS.PUSH,
accessLevelsData: gon.push_access_levels,
$dropdown: this.$allowedToPushDropdown, $dropdown: this.$allowedToPushDropdown,
onSelect: this.onSelectOption.bind(this), data: gon.push_access_levels,
onHide: this.onDropdownHide.bind(this) onSelect: this.onSelect.bind(this)
}); });
} }
onSelectOption(item, $el, dropdownInstance) { onSelect() {
this.hasChanges = true; const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
} const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
onDropdownHide() {
if (!this.hasChanges) return;
this.hasChanges = true; // Do not update if one dropdown has not selected any option
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
this.updatePermissions(); this.$allowedToMergeDropdown.disable();
} this.$allowedToPushDropdown.disable();
updatePermissions() {
const formData = {};
for (const ACCESS_LEVEL in ACCESS_LEVELS) { $.ajax({
const accessLevelName = ACCESS_LEVELS[ACCESS_LEVEL];
formData[`${accessLevelName}_attributes`] = this[`${accessLevelName}_dropdown`].getInputData(accessLevelName);
}
return $.ajax({
type: 'POST', type: 'POST',
url: this.$wrap.data('url'), url: this.$wrap.data('url'),
dataType: 'json', dataType: 'json',
data: { data: {
_method: 'PATCH', _method: 'PATCH',
protected_branch: formData protected_branch: {
}, merge_access_levels_attributes: [{
success: (response) => { id: this.$allowedToMergeDropdown.data('access-level-id'),
this.hasChanges = false; access_level: $allowedToMergeInput.val()
}],
for (const ACCESS_LEVEL in ACCESS_LEVELS) { push_access_levels_attributes: [{
const accessLevelName = ACCESS_LEVELS[ACCESS_LEVEL]; id: this.$allowedToPushDropdown.data('access-level-id'),
access_level: $allowedToPushInput.val()
// The data coming from server will be the new persisted *state* for each dropdown }]
this.setSelectedItemsToDropdown(response[accessLevelName], `${accessLevelName}_dropdown`);
} }
}, },
error() { error() {
...@@ -97,49 +65,5 @@ ...@@ -97,49 +65,5 @@
this.$allowedToPushDropdown.enable(); this.$allowedToPushDropdown.enable();
}); });
} }
setSelectedItemsToDropdown(items = [], dropdownName) {
const itemsToAdd = [];
for (let i = 0; i < items.length; i += 1) {
let itemToAdd;
const currentItem = items[i];
if (currentItem.user_id) {
// Do this only for users for now
// get the current data for selected items
const selectedItems = this[dropdownName].getSelectedItems();
const currentSelectedItem = _.findWhere(selectedItems, { user_id: currentItem.user_id });
itemToAdd = {
id: currentItem.id,
user_id: currentItem.user_id,
type: LEVEL_TYPES.USER,
persisted: true,
name: currentSelectedItem.name,
username: currentSelectedItem.username,
avatar_url: currentSelectedItem.avatar_url
};
} else if (currentItem.group_id) {
itemToAdd = {
id: currentItem.id,
group_id: currentItem.group_id,
type: LEVEL_TYPES.GROUP,
persisted: true
};
} else {
itemToAdd = {
id: currentItem.id,
access_level: currentItem.access_level,
type: LEVEL_TYPES.ROLE,
persisted: true
};
}
itemsToAdd.push(itemToAdd);
}
this[dropdownName].setSelectedItems(itemsToAdd);
}
}; };
})(window); })(window);
...@@ -16,9 +16,6 @@ require('./shortcuts'); ...@@ -16,9 +16,6 @@ require('./shortcuts');
Mousetrap.bind('g p', function() { Mousetrap.bind('g p', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project'); return ShortcutsNavigation.findAndFollowLink('.shortcuts-project');
}); });
Mousetrap.bind('g e', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity');
});
Mousetrap.bind('g f', function() { Mousetrap.bind('g f', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-tree'); return ShortcutsNavigation.findAndFollowLink('.shortcuts-tree');
}); });
...@@ -31,9 +28,6 @@ require('./shortcuts'); ...@@ -31,9 +28,6 @@ require('./shortcuts');
Mousetrap.bind('g n', function() { Mousetrap.bind('g n', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-network'); return ShortcutsNavigation.findAndFollowLink('.shortcuts-network');
}); });
Mousetrap.bind('g g', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs');
});
Mousetrap.bind('g i', function() { Mousetrap.bind('g i', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-issues'); return ShortcutsNavigation.findAndFollowLink('.shortcuts-issues');
}); });
......
...@@ -43,6 +43,8 @@ class UserCallout { ...@@ -43,6 +43,8 @@ class UserCallout {
this.userCalloutBody.append($template); this.userCalloutBody.append($template);
$template.find(closeButton).on('click', e => this.dismissCallout(e)); $template.find(closeButton).on('click', e => this.dismissCallout(e));
$template.find(userCalloutBtn).on('click', e => this.dismissCallout(e)); $template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
} else {
this.userCalloutBody.remove();
} }
} }
...@@ -50,7 +52,7 @@ class UserCallout { ...@@ -50,7 +52,7 @@ class UserCallout {
Cookies.set(USER_CALLOUT_COOKIE, 'true'); Cookies.set(USER_CALLOUT_COOKIE, 'true');
const $currentTarget = $(e.currentTarget); const $currentTarget = $(e.currentTarget);
if ($currentTarget.hasClass('close-user-callout')) { if ($currentTarget.hasClass('close-user-callout')) {
this.userCalloutBody.empty(); this.userCalloutBody.remove();
} }
} }
} }
......
...@@ -11,15 +11,10 @@ $(() => new Vue({ ...@@ -11,15 +11,10 @@ $(() => new Vue({
data() { data() {
const project = document.querySelector('.pipelines'); const project = document.querySelector('.pipelines');
const svgs = document.querySelector('.pipeline-svgs').dataset;
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = gl.utils.DOMStringMapToObject(svgs);
return { return {
scope: project.dataset.url, scope: project.dataset.url,
store: new gl.PipelineStore(), store: new gl.PipelineStore(),
svgs: svgsObject,
}; };
}, },
components: { components: {
...@@ -27,10 +22,8 @@ $(() => new Vue({ ...@@ -27,10 +22,8 @@ $(() => new Vue({
}, },
template: ` template: `
<vue-pipelines <vue-pipelines
:scope='scope' :scope="scope"
:store='store' :store="store">
:svgs='svgs'
>
</vue-pipelines> </vue-pipelines>
`, `,
})); }));
/* global Vue, Flash, gl */ /* global Vue, Flash, gl */
/* eslint-disable no-param-reassign, no-alert */ /* eslint-disable no-param-reassign, no-alert */
const playIconSvg = require('icons/_icon_play.svg');
((gl) => { ((gl) => {
gl.VuePipelineActions = Vue.extend({ gl.VuePipelineActions = Vue.extend({
props: ['pipeline', 'svgs'], props: ['pipeline'],
computed: { computed: {
actions() { actions() {
return this.pipeline.details.manual_actions.length > 0; return this.pipeline.details.manual_actions.length > 0;
...@@ -31,6 +32,11 @@ ...@@ -31,6 +32,11 @@
} }
}, },
}, },
data() {
return { playIconSvg };
},
template: ` template: `
<td class="pipeline-actions"> <td class="pipeline-actions">
<div class="pull-right"> <div class="pull-right">
...@@ -42,7 +48,7 @@ ...@@ -42,7 +48,7 @@
title="Manual job" title="Manual job"
data-placement="top" data-placement="top"
aria-label="Manual job"> aria-label="Manual job">
<span v-html="svgs.iconPlay" aria-hidden="true"></span> <span v-html="playIconSvg" aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
</button> </button>
<ul class="dropdown-menu dropdown-menu-align-right"> <ul class="dropdown-menu dropdown-menu-align-right">
...@@ -50,8 +56,8 @@ ...@@ -50,8 +56,8 @@
<a <a
rel="nofollow" rel="nofollow"
data-method="post" data-method="post"
:href="action.path"> :href="action.path" >
<span v-html="svgs.iconPlay" aria-hidden="true"></span> <span v-html="playIconSvg" aria-hidden="true"></span>
<span>{{action.name}}</span> <span>{{action.name}}</span>
</a> </a>
</li> </li>
......
...@@ -27,7 +27,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s ...@@ -27,7 +27,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
pageRequest: false, pageRequest: false,
}; };
}, },
props: ['scope', 'store', 'svgs'], props: ['scope', 'store'],
created() { created() {
const pagenum = gl.utils.getParameterByName('page'); const pagenum = gl.utils.getParameterByName('page');
const scope = gl.utils.getParameterByName('scope'); const scope = gl.utils.getParameterByName('scope');
...@@ -70,10 +70,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s ...@@ -70,10 +70,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
</div> </div>
<div class="table-holder" v-if='!pageRequest && pipelines.length'> <div class="table-holder" v-if='!pageRequest && pipelines.length'>
<pipelines-table-component <pipelines-table-component :pipelines='pipelines'/>
:pipelines='pipelines'
:svgs='svgs'>
</pipelines-table-component>
</div> </div>
<gl-pagination <gl-pagination
......
/* global Vue, Flash, gl */ /* global Vue, Flash, gl */
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import canceledSvg from 'icons/_icon_status_canceled_borderless.svg';
import createdSvg from 'icons/_icon_status_created_borderless.svg';
import failedSvg from 'icons/_icon_status_failed_borderless.svg';
import manualSvg from 'icons/_icon_status_manual_borderless.svg';
import pendingSvg from 'icons/_icon_status_pending_borderless.svg';
import runningSvg from 'icons/_icon_status_running_borderless.svg';
import skippedSvg from 'icons/_icon_status_skipped_borderless.svg';
import successSvg from 'icons/_icon_status_success_borderless.svg';
import warningSvg from 'icons/_icon_status_warning_borderless.svg';
((gl) => { ((gl) => {
gl.VueStage = Vue.extend({ gl.VueStage = Vue.extend({
data() { data() {
const svgsDictionary = {
icon_status_canceled: canceledSvg,
icon_status_created: createdSvg,
icon_status_failed: failedSvg,
icon_status_manual: manualSvg,
icon_status_pending: pendingSvg,
icon_status_running: runningSvg,
icon_status_skipped: skippedSvg,
icon_status_success: successSvg,
icon_status_warning: warningSvg,
};
return { return {
builds: '', builds: '',
spinner: '<span class="fa fa-spinner fa-spin"></span>', spinner: '<span class="fa fa-spinner fa-spin"></span>',
svg: svgsDictionary[this.stage.status.icon],
}; };
}, },
props: { props: {
stage: { stage: {
type: Object, type: Object,
required: true, required: true,
}, },
svgs: {
type: Object,
required: true,
},
match: {
type: Function,
required: true,
},
}, },
updated() { updated() {
...@@ -73,11 +88,6 @@ ...@@ -73,11 +88,6 @@
tooltip() { tooltip() {
return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`; return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`;
}, },
svg() {
const { icon } = this.stage.status;
const stageIcon = icon.replace(/icon/i, 'stage_icon');
return this.svgs[this.match(stageIcon)];
},
triggerButtonClass() { triggerButtonClass() {
return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`; return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`;
}, },
...@@ -91,8 +101,7 @@ ...@@ -91,8 +101,7 @@
data-placement="top" data-placement="top"
data-toggle="dropdown" data-toggle="dropdown"
type="button" type="button"
:aria-label="stage.title" :aria-label="stage.title">
>
<span v-html="svg" aria-hidden="true"></span> <span v-html="svg" aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
</button> </button>
...@@ -101,8 +110,7 @@ ...@@ -101,8 +110,7 @@
<div <div
:class="dropdownClass" :class="dropdownClass"
class="js-builds-dropdown-list scrollable-menu" class="js-builds-dropdown-list scrollable-menu"
v-html="buildsOrSpinner" v-html="buildsOrSpinner">
>
</div> </div>
</ul> </ul>
</div> </div>
......
...@@ -4,14 +4,17 @@ ...@@ -4,14 +4,17 @@
window.Vue = require('vue'); window.Vue = require('vue');
require('../lib/utils/datetime_utility'); require('../lib/utils/datetime_utility');
const iconTimerSvg = require('../../../views/shared/icons/_icon_timer.svg');
((gl) => { ((gl) => {
gl.VueTimeAgo = Vue.extend({ gl.VueTimeAgo = Vue.extend({
data() { data() {
return { return {
currentTime: new Date(), currentTime: new Date(),
iconTimerSvg,
}; };
}, },
props: ['pipeline', 'svgs'], props: ['pipeline'],
computed: { computed: {
timeAgo() { timeAgo() {
return gl.utils.getTimeago(); return gl.utils.getTimeago();
...@@ -56,7 +59,7 @@ require('../lib/utils/datetime_utility'); ...@@ -56,7 +59,7 @@ require('../lib/utils/datetime_utility');
template: ` template: `
<td class="pipelines-time-ago"> <td class="pipelines-time-ago">
<p class="duration" v-if='duration'> <p class="duration" v-if='duration'>
<span v-html='svgs.iconTimer'></span> <span v-html="iconTimerSvg"></span>
{{duration}} {{duration}}
</p> </p>
<p class="finished-at" v-if='timeStopped'> <p class="finished-at" v-if='timeStopped'>
......
/* global Vue */ /* global Vue */
window.Vue = require('vue'); window.Vue = require('vue');
const commitIconSvg = require('icons/_icon_commit.svg');
(() => { (() => {
window.gl = window.gl || {}; window.gl = window.gl || {};
...@@ -69,11 +70,6 @@ window.Vue = require('vue'); ...@@ -69,11 +70,6 @@ window.Vue = require('vue');
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
commitIconSvg: {
type: String,
required: false,
},
}, },
computed: { computed: {
...@@ -116,6 +112,10 @@ window.Vue = require('vue'); ...@@ -116,6 +112,10 @@ window.Vue = require('vue');
}, },
}, },
data() {
return { commitIconSvg };
},
template: ` template: `
<div class="branch-commit"> <div class="branch-commit">
......
...@@ -21,14 +21,6 @@ require('./pipelines_table_row'); ...@@ -21,14 +21,6 @@ require('./pipelines_table_row');
default: () => ([]), default: () => ([]),
}, },
/**
* TODO: Remove this when we have webpack.
*/
svgs: {
type: Object,
required: true,
default: () => ({}),
},
}, },
components: { components: {
...@@ -51,8 +43,7 @@ require('./pipelines_table_row'); ...@@ -51,8 +43,7 @@ require('./pipelines_table_row');
<template v-for="model in pipelines" <template v-for="model in pipelines"
v-bind:model="model"> v-bind:model="model">
<tr is="pipelines-table-row-component" <tr is="pipelines-table-row-component"
:pipeline="model" :pipeline="model"></tr>
:svgs="svgs"></tr>
</template> </template>
</tbody> </tbody>
</table> </table>
......
...@@ -25,14 +25,6 @@ require('./commit'); ...@@ -25,14 +25,6 @@ require('./commit');
default: () => ({}), default: () => ({}),
}, },
/**
* TODO: Remove this when we have webpack;
*/
svgs: {
type: Object,
required: true,
default: () => ({}),
},
}, },
components: { components: {
...@@ -174,30 +166,9 @@ require('./commit'); ...@@ -174,30 +166,9 @@ require('./commit');
}, },
}, },
methods: {
/**
* FIXME: This should not be in this component but in the components that
* need this function.
*
* Used to render SVGs in the following components:
* - status-scope
* - dropdown-stage
*
* @param {String} string
* @return {String}
*/
match(string) {
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
},
},
template: ` template: `
<tr class="commit"> <tr class="commit">
<status-scope <status-scope :pipeline="pipeline"/>
:pipeline="pipeline"
:svgs="svgs"
:match="match">
</status-scope>
<pipeline-url :pipeline="pipeline"></pipeline-url> <pipeline-url :pipeline="pipeline"></pipeline-url>
...@@ -208,26 +179,20 @@ require('./commit'); ...@@ -208,26 +179,20 @@ require('./commit');
:commit-url="commitUrl" :commit-url="commitUrl"
:short-sha="commitShortSha" :short-sha="commitShortSha"
:title="commitTitle" :title="commitTitle"
:author="commitAuthor" :author="commitAuthor"/>
:commit-icon-svg="svgs.commitIconSvg">
</commit-component>
</td> </td>
<td class="stage-cell"> <td class="stage-cell">
<div class="stage-container dropdown js-mini-pipeline-graph" <div class="stage-container dropdown js-mini-pipeline-graph"
v-if="pipeline.details.stages.length > 0" v-if="pipeline.details.stages.length > 0"
v-for="stage in pipeline.details.stages"> v-for="stage in pipeline.details.stages">
<dropdown-stage <dropdown-stage :stage="stage"/>
:stage="stage"
:svgs="svgs"
:match="match">
</dropdown-stage>
</div> </div>
</td> </td>
<time-ago :pipeline="pipeline" :svgs="svgs"></time-ago> <time-ago :pipeline="pipeline"/>
<pipeline-actions :pipeline="pipeline" :svgs="svgs"></pipeline-actions> <pipeline-actions :pipeline="pipeline" />
</tr> </tr>
`, `,
}); });
......
...@@ -19,7 +19,6 @@ window.Vue = require('vue'); ...@@ -19,7 +19,6 @@ window.Vue = require('vue');
/** /**
This function will take the information given by the pagination component This function will take the information given by the pagination component
And make a new Turbolinks call
Here is an example `change` method: Here is an example `change` method:
......
...@@ -107,11 +107,12 @@ ...@@ -107,11 +107,12 @@
&.fa-spinner { &.fa-spinner {
font-size: 16px; font-size: 16px;
margin-top: -8px; margin-top: -3px;
} }
} }
.fa-chevron-down { .fa-chevron-down,
.fa-spinner {
position: absolute; position: absolute;
top: 11px; top: 11px;
right: 8px; right: 8px;
...@@ -196,6 +197,10 @@ ...@@ -196,6 +197,10 @@
&.is-focused { &.is-focused {
background-color: $dropdown-link-hover-bg; background-color: $dropdown-link-hover-bg;
text-decoration: none; text-decoration: none;
.badge {
background-color: darken($row-hover, 5%);
}
} }
&.dropdown-menu-empty-link { &.dropdown-menu-empty-link {
...@@ -232,6 +237,12 @@ ...@@ -232,6 +237,12 @@
padding: 5px 8px; padding: 5px 8px;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
} }
.badge {
position: absolute;
right: 8px;
top: 5px;
}
} }
.dropdown-menu-drop-up { .dropdown-menu-drop-up {
......
...@@ -149,14 +149,14 @@ header { ...@@ -149,14 +149,14 @@ header {
.header-logo { .header-logo {
display: inline-block; display: inline-block;
margin: 0 8px 0 3px; margin: 0 7px 0 2px;
position: relative; position: relative;
top: 7px; top: 10px;
transition-duration: .3s; transition-duration: .3s;
svg, svg,
img { img {
height: 36px; height: 28px;
} }
&:hover { &:hover {
......
...@@ -73,10 +73,6 @@ ...@@ -73,10 +73,6 @@
right: $gutter_collapsed_width; right: $gutter_collapsed_width;
} }
} }
&.with-overlay {
padding-right: $gutter_collapsed_width;
}
} }
.right-sidebar { .right-sidebar {
......
...@@ -155,7 +155,7 @@ ...@@ -155,7 +155,7 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.event-item { .event-item {
padding-left: $gl-padding; padding-left: 0;
.event-title { .event-title {
white-space: normal; white-space: normal;
...@@ -169,8 +169,7 @@ ...@@ -169,8 +169,7 @@
.event-body { .event-body {
margin: 0; margin: 0;
border-left: 2px solid $events-body-border; padding-left: 0;
padding-left: 10px;
} }
.event-item-timestamp { .event-item-timestamp {
......
...@@ -279,7 +279,7 @@ table.u2f-registrations { ...@@ -279,7 +279,7 @@ table.u2f-registrations {
} }
.user-callout { .user-callout {
margin: 24px auto 0; margin: 0 auto;
.bordered-box { .bordered-box {
border: 1px solid $border-color; border: 1px solid $border-color;
...@@ -287,6 +287,7 @@ table.u2f-registrations { ...@@ -287,6 +287,7 @@ table.u2f-registrations {
} }
.landing { .landing {
margin-top: $gl-padding;
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
.close { .close {
......
...@@ -182,7 +182,8 @@ input[type="checkbox"]:hover { ...@@ -182,7 +182,8 @@ input[type="checkbox"]:hover {
display: flex; display: flex;
} }
.search-field-holder { .search-field-holder,
.project-filter-form {
-webkit-flex: 1 0 auto; -webkit-flex: 1 0 auto;
flex: 1 0 auto; flex: 1 0 auto;
position: relative; position: relative;
...@@ -201,7 +202,8 @@ input[type="checkbox"]:hover { ...@@ -201,7 +202,8 @@ input[type="checkbox"]:hover {
pointer-events: none; pointer-events: none;
} }
.search-text-input { .search-text-input,
.project-filter-form-field {
padding-left: $gl-padding + 15px; padding-left: $gl-padding + 15px;
padding-right: $gl-padding + 15px; padding-right: $gl-padding + 15px;
} }
......
...@@ -178,3 +178,29 @@ ...@@ -178,3 +178,29 @@
margin-left: $btn-side-margin; margin-left: $btn-side-margin;
} }
} }
.repo-charts {
.sub-header {
margin: 20px 0;
}
.sub-header-block.border-top {
margin-top: 20px;
padding: 0;
border-top: 1px solid $white-dark;
border-bottom: none;
}
.commit-stats li {
font-size: 16px;
}
.tree-ref-header {
margin-bottom: 20px;
h4 {
margin: 0;
line-height: 36px;
}
}
}
class Admin::HealthCheckController < Admin::ApplicationController class Admin::HealthCheckController < Admin::ApplicationController
def show def show
@errors = HealthCheck::Utils.process_checks('standard') @errors = HealthCheck::Utils.process_checks(['standard'])
end end
end end
...@@ -14,6 +14,15 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -14,6 +14,15 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("admin/projects/_projects", locals: { projects: @projects })
}
end
end
end end
def show def show
......
...@@ -126,10 +126,6 @@ class ApplicationController < ActionController::Base ...@@ -126,10 +126,6 @@ class ApplicationController < ActionController::Base
headers['X-XSS-Protection'] = '1; mode=block' headers['X-XSS-Protection'] = '1; mode=block'
headers['X-UA-Compatible'] = 'IE=edge' headers['X-UA-Compatible'] = 'IE=edge'
headers['X-Content-Type-Options'] = 'nosniff' headers['X-Content-Type-Options'] = 'nosniff'
# Enabling HSTS for non-standard ports would send clients to the wrong port
if Gitlab.config.gitlab.https && Gitlab.config.gitlab.port == 443
headers['Strict-Transport-Security'] = 'max-age=31536000'
end
end end
def validate_user_service_ticket! def validate_user_service_ticket!
......
module Ci
class ProjectsController < ::ApplicationController
before_action :project
before_action :no_cache, only: [:badge]
before_action :authorize_read_project!, except: [:badge, :index]
skip_before_action :authenticate_user!, only: [:badge]
protect_from_forgery
def index
redirect_to root_path
end
def show
# Temporary compatibility with CI badges pointing to CI project page
redirect_to namespace_project_path(project.namespace, project)
end
# Project status badge
# Image with build status for sha or ref
#
# This action in DEPRECATED, this is here only for backwards compatibility
# with projects migrated from GitLab CI.
#
def badge
return render_404 unless @project
image = Ci::ImageForBuildService.new.execute(@project, params)
send_file image.path, filename: image.name, disposition: 'inline', type: "image/svg+xml"
end
protected
def project
@project ||= Project.find_by(ci_id: params[:id].to_i)
end
def no_cache
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
def authorize_read_project!
return access_denied! unless can?(current_user, :read_project, project)
end
end
end
...@@ -4,10 +4,9 @@ module CreatesCommit ...@@ -4,10 +4,9 @@ module CreatesCommit
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil) def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
set_commit_variables set_commit_variables
start_branch = @mr_target_branch unless initial_commit?
commit_params = @commit_params.merge( commit_params = @commit_params.merge(
start_project: @mr_target_project, start_project: @mr_target_project,
start_branch: start_branch, start_branch: @mr_target_branch,
target_branch: @mr_source_branch target_branch: @mr_source_branch
) )
...@@ -17,12 +16,16 @@ module CreatesCommit ...@@ -17,12 +16,16 @@ module CreatesCommit
if result[:status] == :success if result[:status] == :success
update_flash_notice(success_notice) update_flash_notice(success_notice)
success_path = final_success_path(success_path)
respond_to do |format| respond_to do |format|
format.html { redirect_to final_success_path(success_path) } format.html { redirect_to success_path }
format.json { render json: { message: "success", filePath: final_success_path(success_path) } } format.json { render json: { message: "success", filePath: success_path } }
end end
else else
flash[:alert] = result[:message] flash[:alert] = result[:message]
failure_path = failure_path.call if failure_path.respond_to?(:call)
respond_to do |format| respond_to do |format|
format.html do format.html do
if failure_view if failure_view
...@@ -58,9 +61,13 @@ module CreatesCommit ...@@ -58,9 +61,13 @@ module CreatesCommit
end end
def final_success_path(success_path) def final_success_path(success_path)
return success_path unless create_merge_request? if create_merge_request?
merge_request_exists? ? existing_merge_request_path : new_merge_request_path merge_request_exists? ? existing_merge_request_path : new_merge_request_path
else
success_path = success_path.call if success_path.respond_to?(:call)
success_path
end
end end
def new_merge_request_path def new_merge_request_path
...@@ -92,47 +99,26 @@ module CreatesCommit ...@@ -92,47 +99,26 @@ module CreatesCommit
end end
def create_merge_request? def create_merge_request?
# XXX: Even if the field is set, if we're checking the same branch # Even if the field is set, if we're checking the same branch
# as the target branch in the same project, # as the target branch in the same project,
# we don't want to create a merge request. # we don't want to create a merge request.
params[:create_merge_request].present? && params[:create_merge_request].present? &&
(different_project? || @ref != @target_branch) (different_project? || @mr_target_branch != @mr_source_branch)
end end
# TODO: We should really clean this up
def set_commit_variables def set_commit_variables
@mr_source_project =
if can?(current_user, :push_code, @project) if can?(current_user, :push_code, @project)
# Edit file in this project @mr_source_project = @project
@project @target_branch ||= @ref
else else
# Merge request from fork to this project @mr_source_project = current_user.fork_of(@project)
current_user.fork_of(@project) @target_branch ||= @mr_source_project.repository.next_branch('patch')
end end
# Merge request to this project # Merge request to this project
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch = @ref || @target_branch @mr_target_branch ||= @ref || @target_branch
@mr_source_branch = guess_mr_source_branch
end
def initial_commit?
@mr_target_branch.nil? ||
!@mr_target_project.repository.branch_exists?(@mr_target_branch)
end
def guess_mr_source_branch
# XXX: Happens when viewing a commit without a branch. In this case,
# @target_branch would be the default branch for @mr_source_project,
# however we want a generated new branch here. Thus we can't use
# @target_branch, but should pass nil to indicate that we want a new
# branch instead of @target_branch.
return if
create_merge_request? &&
# XXX: Don't understand why rubocop prefers this indention
@mr_source_project.repository.branch_exists?(@target_branch)
@target_branch @mr_source_branch = @target_branch
end end
end end
...@@ -8,7 +8,7 @@ module FilterProjects ...@@ -8,7 +8,7 @@ module FilterProjects
extend ActiveSupport::Concern extend ActiveSupport::Concern
def filter_projects(projects) def filter_projects(projects)
projects = projects.search(params[:filter_projects]) if params[:filter_projects].present? projects = projects.search(params[:name]) if params[:name].present?
projects = projects.non_archived if params[:archived].blank? projects = projects.non_archived if params[:archived].blank?
projects = projects.personal(current_user) if params[:personal].present? && current_user projects = projects.personal(current_user) if params[:personal].present? && current_user
......
class Dashboard::GroupsController < Dashboard::ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
def index def index
@group_members = current_user.group_members.includes(source: :route).page(params[:page]) @group_members = current_user.group_members.includes(source: :route).joins(:group)
@group_members = @group_members.merge(Group.search(params[:filter_groups])) if params[:filter_groups].present?
@group_members = @group_members.merge(Group.sort(@sort = params[:sort]))
@group_members = @group_members.page(params[:page])
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("dashboard/groups/_groups", locals: { group_members: @group_members })
}
end
end
end end
end end
class Explore::GroupsController < Explore::ApplicationController class Explore::GroupsController < Explore::ApplicationController
def index def index
@groups = GroupsFinder.new.execute(current_user) @groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]) @groups = @groups.page(params[:page])
respond_to do |format|
format.html
format.json do
render json: {
html: view_to_html_string("explore/groups/_groups", locals: { groups: @groups })
}
end
end
end end
end end
...@@ -108,7 +108,7 @@ class GroupsController < Groups::ApplicationController ...@@ -108,7 +108,7 @@ class GroupsController < Groups::ApplicationController
@projects = @projects.sorted_by_activity @projects = @projects.sorted_by_activity
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]) if params[:filter_projects].blank? @projects = @projects.page(params[:page]) if params[:name].blank?
end end
def authorize_create_group! def authorize_create_group!
......
...@@ -24,7 +24,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -24,7 +24,7 @@ class Projects::BlobController < Projects::ApplicationController
def create def create
create_commit(Files::CreateService, success_notice: "The file has been successfully created.", create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
success_path: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)), success_path: -> { namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) },
failure_view: :new, failure_view: :new,
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref)) failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end end
...@@ -40,7 +40,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -40,7 +40,7 @@ class Projects::BlobController < Projects::ApplicationController
def update def update
@path = params[:file_path] if params[:file_path].present? @path = params[:file_path] if params[:file_path].present?
create_commit(Files::UpdateService, success_path: after_edit_path, create_commit(Files::UpdateService, success_path: -> { after_edit_path },
failure_view: :edit, failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
...@@ -62,7 +62,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -62,7 +62,7 @@ class Projects::BlobController < Projects::ApplicationController
def destroy def destroy
create_commit(Files::DestroyService, success_notice: "The file has been successfully deleted.", create_commit(Files::DestroyService, success_notice: "The file has been successfully deleted.",
success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch), success_path: -> { namespace_project_tree_path(@project.namespace, @project, @target_branch) },
failure_view: :show, failure_view: :show,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
end end
......
...@@ -51,23 +51,35 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -51,23 +51,35 @@ class Projects::CommitController < Projects::ApplicationController
def revert def revert
assign_change_commit_vars assign_change_commit_vars
return render_404 if @target_branch.blank? return render_404 if @start_branch.blank?
@target_branch = create_new_branch? ? @commit.revert_branch_name : @start_branch
@mr_target_branch = @start_branch
create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.", create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.",
success_path: successful_change_path, failure_path: failed_change_path) success_path: -> { successful_change_path }, failure_path: failed_change_path)
end end
def cherry_pick def cherry_pick
assign_change_commit_vars assign_change_commit_vars
return render_404 if @target_branch.blank? return render_404 if @start_branch.blank?
@target_branch = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch
@mr_target_branch = @start_branch
create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.", create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.",
success_path: successful_change_path, failure_path: failed_change_path) success_path: -> { successful_change_path }, failure_path: failed_change_path)
end end
private private
def create_new_branch?
params[:create_merge_request].present? || !can?(current_user, :push_code, @project)
end
def successful_change_path def successful_change_path
referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @target_branch) referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @target_branch)
end end
...@@ -78,7 +90,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -78,7 +90,7 @@ class Projects::CommitController < Projects::ApplicationController
def referenced_merge_request_url def referenced_merge_request_url
if merge_request = @commit.merged_merge_request(current_user) if merge_request = @commit.merged_merge_request(current_user)
namespace_project_merge_request_url(@project.namespace, @project, merge_request) namespace_project_merge_request_url(merge_request.target_project.namespace, merge_request.target_project, merge_request)
end end
end end
...@@ -118,11 +130,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -118,11 +130,7 @@ class Projects::CommitController < Projects::ApplicationController
end end
def assign_change_commit_vars def assign_change_commit_vars
@commit = project.commit(params[:id]) @start_branch = params[:start_branch]
@target_branch = params[:target_branch] @commit_params = { commit: @commit }
@commit_params = {
commit: @commit,
create_merge_request: params[:create_merge_request].present? || different_project?
}
end end
end end
...@@ -17,6 +17,25 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -17,6 +17,25 @@ class Projects::GraphsController < Projects::ApplicationController
end end
def commits def commits
redirect_to action: 'charts'
end
def languages
redirect_to action: 'charts'
end
def charts
get_commits
get_languages
end
def ci
redirect_to charts_namespace_project_pipelines_path(@project.namespace, @project)
end
private
def get_commits
@commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true) @commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true)
@commits_graph = Gitlab::Graphs::Commits.new(@commits) @commits_graph = Gitlab::Graphs::Commits.new(@commits)
@commits_per_week_days = @commits_graph.commits_per_week_days @commits_per_week_days = @commits_graph.commits_per_week_days
...@@ -24,15 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -24,15 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController
@commits_per_month = @commits_graph.commits_per_month @commits_per_month = @commits_graph.commits_per_month
end end
def ci def get_languages
@charts = {}
@charts[:week] = Ci::Charts::WeekChart.new(project)
@charts[:month] = Ci::Charts::MonthChart.new(project)
@charts[:year] = Ci::Charts::YearChart.new(project)
@charts[:build_times] = Ci::Charts::BuildTime.new(project)
end
def languages
@languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages @languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
total = @languages.map(&:last).sum total = @languages.map(&:last).sum
...@@ -52,8 +63,6 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -52,8 +63,6 @@ class Projects::GraphsController < Projects::ApplicationController
end end
end end
private
def fetch_graph def fetch_graph
@commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true) @commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true)
@log = [] @log = []
......
class Projects::PipelinesController < Projects::ApplicationController class Projects::PipelinesController < Projects::ApplicationController
before_action :pipeline, except: [:index, :new, :create] before_action :pipeline, except: [:index, :new, :create, :charts]
before_action :commit, only: [:show, :builds] before_action :commit, only: [:show, :builds]
before_action :authorize_read_pipeline! before_action :authorize_read_pipeline!
before_action :authorize_create_pipeline!, only: [:new, :create] before_action :authorize_create_pipeline!, only: [:new, :create]
before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action :builds_enabled, only: :charts
def index def index
@scope = params[:scope] @scope = params[:scope]
...@@ -92,6 +93,14 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -92,6 +93,14 @@ class Projects::PipelinesController < Projects::ApplicationController
redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project) redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project)
end end
def charts
@charts = {}
@charts[:week] = Ci::Charts::WeekChart.new(project)
@charts[:month] = Ci::Charts::MonthChart.new(project)
@charts[:year] = Ci::Charts::YearChart.new(project)
@charts[:build_times] = Ci::Charts::BuildTime.new(project)
end
private private
def create_params def create_params
......
...@@ -170,7 +170,7 @@ module ApplicationHelper ...@@ -170,7 +170,7 @@ module ApplicationHelper
css_classes = short_format ? 'js-short-timeago' : 'js-timeago' css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
css_classes << " #{html_class}" unless html_class.blank? css_classes << " #{html_class}" unless html_class.blank?
element = content_tag :time, time.to_s, element = content_tag :time, time.strftime("%b %d, %Y"),
class: css_classes, class: css_classes,
title: time.to_time.in_time_zone.to_s(:medium), title: time.to_time.in_time_zone.to_s(:medium),
datetime: time.to_time.getutc.iso8601, datetime: time.to_time.getutc.iso8601,
......
module ExploreHelper module ExploreHelper
def filter_projects_path(options = {}) def filter_projects_path(options = {})
exist_opts = { exist_opts = {
sort: params[:sort], sort: params[:sort] || @sort,
scope: params[:scope], scope: params[:scope],
group: params[:group], group: params[:group],
tag: params[:tag], tag: params[:tag],
visibility_level: params[:visibility_level], visibility_level: params[:visibility_level],
name: params[:name],
personal: params[:personal],
archived: params[:archived],
shared: params[:shared],
namespace_id: params[:namespace_id],
} }
options = exist_opts.merge(options) options = exist_opts.merge(options).delete_if { |key, value| value.blank? }
path = request.path request_path_with_options(options)
path << "?#{options.to_param}" end
path
def filter_groups_path(options = {})
request_path_with_options(options)
end end
def explore_controller? def explore_controller?
controller.class.name.split("::").first == "Explore" controller.class.name.split("::").first == "Explore"
end end
private
def request_path_with_options(options = {})
request.path + "?#{options.to_param}"
end
end end
...@@ -35,9 +35,9 @@ module PreferencesHelper ...@@ -35,9 +35,9 @@ module PreferencesHelper
def project_view_choices def project_view_choices
[ [
['Readme (default)', :readme], ['Readme', :readme],
['Activity view', :activity], ['Activity view', :activity],
['Files view', :files] ['Files and Readme (default)', :files]
] ]
end end
......
module RssHelper
def rss_url_options
{ format: :atom, private_token: current_user.try(:private_token) }
end
end
...@@ -114,7 +114,11 @@ class Project < ActiveRecord::Base ...@@ -114,7 +114,11 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
has_one :external_wiki_service, dependent: :destroy has_one :external_wiki_service, dependent: :destroy
has_one :kubernetes_service, dependent: :destroy, inverse_of: :project has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
<<<<<<< HEAD
has_one :index_status, dependent: :destroy has_one :index_status, dependent: :destroy
=======
has_one :mock_ci_service, dependent: :destroy
>>>>>>> ce-com/master
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link has_one :forked_from_project, through: :forked_project_link
......
...@@ -94,7 +94,12 @@ class KubernetesService < DeploymentService ...@@ -94,7 +94,12 @@ class KubernetesService < DeploymentService
{ key: 'KUBE_TOKEN', value: token, public: false }, { key: 'KUBE_TOKEN', value: token, public: false },
{ key: 'KUBE_NAMESPACE', value: namespace, public: true } { key: 'KUBE_NAMESPACE', value: namespace, public: true }
] ]
variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true } if ca_pem.present?
if ca_pem.present?
variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true }
variables << { key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true }
end
variables variables
end end
......
...@@ -10,6 +10,7 @@ class Repository ...@@ -10,6 +10,7 @@ class Repository
attr_accessor :path_with_namespace, :project attr_accessor :path_with_namespace, :project
CommitError = Class.new(StandardError) CommitError = Class.new(StandardError)
CreateTreeError = Class.new(StandardError)
MIRROR_REMOTE = "upstream".freeze MIRROR_REMOTE = "upstream".freeze
MIRROR_GEO = "geo".freeze MIRROR_GEO = "geo".freeze
...@@ -897,17 +898,18 @@ class Repository ...@@ -897,17 +898,18 @@ class Repository
end end
def revert( def revert(
user, commit, branch_name, revert_tree_id = nil, user, commit, branch_name,
start_branch_name: nil, start_project: project) start_branch_name: nil, start_project: project)
revert_tree_id ||= check_revert_content(commit, branch_name)
return false unless revert_tree_id
GitOperationService.new(user, self).with_branch( GitOperationService.new(user, self).with_branch(
branch_name, branch_name,
start_branch_name: start_branch_name, start_branch_name: start_branch_name,
start_project: start_project) do |start_commit| start_project: start_project) do |start_commit|
revert_tree_id = check_revert_content(commit, start_commit.sha)
unless revert_tree_id
raise Repository::CreateTreeError.new('Failed to revert commit')
end
committer = user_to_committer(user) committer = user_to_committer(user)
Rugged::Commit.create(rugged, Rugged::Commit.create(rugged,
...@@ -920,17 +922,18 @@ class Repository ...@@ -920,17 +922,18 @@ class Repository
end end
def cherry_pick( def cherry_pick(
user, commit, branch_name, cherry_pick_tree_id = nil, user, commit, branch_name,
start_branch_name: nil, start_project: project) start_branch_name: nil, start_project: project)
cherry_pick_tree_id ||= check_cherry_pick_content(commit, branch_name)
return false unless cherry_pick_tree_id
GitOperationService.new(user, self).with_branch( GitOperationService.new(user, self).with_branch(
branch_name, branch_name,
start_branch_name: start_branch_name, start_branch_name: start_branch_name,
start_project: start_project) do |start_commit| start_project: start_project) do |start_commit|
cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
unless cherry_pick_tree_id
raise Repository::CreateTreeError.new('Failed to cherry-pick commit')
end
committer = user_to_committer(user) committer = user_to_committer(user)
Rugged::Commit.create(rugged, Rugged::Commit.create(rugged,
...@@ -954,8 +957,7 @@ class Repository ...@@ -954,8 +957,7 @@ class Repository
end end
end end
def check_revert_content(target_commit, branch_name) def check_revert_content(target_commit, source_sha)
source_sha = commit(branch_name).sha
args = [target_commit.sha, source_sha] args = [target_commit.sha, source_sha]
args << { mainline: 1 } if target_commit.merge_commit? args << { mainline: 1 } if target_commit.merge_commit?
...@@ -968,8 +970,7 @@ class Repository ...@@ -968,8 +970,7 @@ class Repository
tree_id tree_id
end end
def check_cherry_pick_content(target_commit, branch_name) def check_cherry_pick_content(target_commit, source_sha)
source_sha = commit(branch_name).sha
args = [target_commit.sha, source_sha] args = [target_commit.sha, source_sha]
args << 1 if target_commit.merge_commit? args << 1 if target_commit.merge_commit?
...@@ -1078,6 +1079,8 @@ class Repository ...@@ -1078,6 +1079,8 @@ class Repository
end end
def with_repo_branch_commit(start_repository, start_branch_name) def with_repo_branch_commit(start_repository, start_branch_name)
return yield(nil) if start_repository.empty_repo?
branch_name_or_sha = branch_name_or_sha =
if start_repository == self if start_repository == self
start_branch_name start_branch_name
......
...@@ -23,6 +23,7 @@ class User < ActiveRecord::Base ...@@ -23,6 +23,7 @@ class User < ActiveRecord::Base
default_value_for :can_create_team, false default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false default_value_for :hide_no_ssh_key, false
default_value_for :hide_no_password, false default_value_for :hide_no_password, false
default_value_for :project_view, :files
attr_encrypted :otp_secret, attr_encrypted :otp_secret,
key: Gitlab::Application.secrets.otp_key_base, key: Gitlab::Application.secrets.otp_key_base,
......
...@@ -34,8 +34,6 @@ class GroupPolicy < BasePolicy ...@@ -34,8 +34,6 @@ class GroupPolicy < BasePolicy
if globally_viewable && @subject.request_access_enabled && !member if globally_viewable && @subject.request_access_enabled && !member
can! :request_access can! :request_access
end end
additional_rules!(master)
end end
def can_read_group? def can_read_group?
...@@ -47,6 +45,7 @@ class GroupPolicy < BasePolicy ...@@ -47,6 +45,7 @@ class GroupPolicy < BasePolicy
GroupProjectsFinder.new(@subject).execute(@user).any? GroupProjectsFinder.new(@subject).execute(@user).any?
end end
<<<<<<< HEAD
def additional_rules!(master) def additional_rules!(master)
if @subject.ldap_synced? if @subject.ldap_synced?
...@@ -54,4 +53,6 @@ class GroupPolicy < BasePolicy ...@@ -54,4 +53,6 @@ class GroupPolicy < BasePolicy
can! :override_group_member if master can! :override_group_member if master
end end
end end
=======
>>>>>>> ce-com/master
end end
module Ci
class ImageForBuildService
def execute(project, opts)
ref = opts[:ref]
sha = opts[:sha] || ref_sha(project, ref)
pipelines = project.pipelines.where(sha: sha)
image_name = image_for_status(pipelines.latest_status(ref))
image_path = Rails.root.join('public/ci', image_name)
OpenStruct.new(path: image_path, name: image_name)
end
private
def ref_sha(project, ref)
project.commit(ref).try(:sha) if ref
end
def image_for_status(status)
status ||= 'unknown'
'build-' + status + ".svg"
end
end
end
module Ci module Ci
class RetryBuildService < ::BaseService class RetryBuildService < ::BaseService
CLONE_ATTRIBUTES = %i[pipeline project ref tag options commands name CLONE_ACCESSORS = %i[pipeline project ref tag options commands name
allow_failure stage stage_idx trigger_request allow_failure stage stage_idx trigger_request
yaml_variables when environment coverage_regex] yaml_variables when environment coverage_regex
.freeze description tag_list].freeze
REJECT_ATTRIBUTES = %i[id status user token coverage trace runner
artifacts_expire_at artifacts_file
artifacts_metadata artifacts_size
created_at updated_at started_at finished_at
queued_at erased_by erased_at].freeze
IGNORE_ATTRIBUTES = %i[type lock_version gl_project_id target_url
deploy job_id description].freeze
def execute(build) def execute(build)
reprocess(build).tap do |new_build| reprocess(build).tap do |new_build|
...@@ -31,7 +22,7 @@ module Ci ...@@ -31,7 +22,7 @@ module Ci
raise Gitlab::Access::AccessDeniedError raise Gitlab::Access::AccessDeniedError
end end
attributes = CLONE_ATTRIBUTES.map do |attribute| attributes = CLONE_ACCESSORS.map do |attribute|
[attribute, build.send(attribute)] [attribute, build.send(attribute)]
end end
......
...@@ -8,9 +8,9 @@ module Commits ...@@ -8,9 +8,9 @@ module Commits
@start_branch = params[:start_branch] @start_branch = params[:start_branch]
@target_branch = params[:target_branch] @target_branch = params[:target_branch]
@commit = params[:commit] @commit = params[:commit]
@create_merge_request = params[:create_merge_request].present?
check_push_permissions unless @create_merge_request check_push_permissions
commit commit
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError,
ValidationError, ChangeError => ex ValidationError, ChangeError => ex
...@@ -26,35 +26,22 @@ module Commits ...@@ -26,35 +26,22 @@ module Commits
def commit_change(action) def commit_change(action)
raise NotImplementedError unless repository.respond_to?(action) raise NotImplementedError unless repository.respond_to?(action)
if @create_merge_request validate_target_branch if different_branch?
into = @commit.public_send("#{action}_branch_name")
tree_branch = @start_branch
else
into = tree_branch = @target_branch
end
tree_id = repository.public_send(
"check_#{action}_content", @commit, tree_branch)
if tree_id
validate_target_branch(into) if @create_merge_request
repository.public_send( repository.public_send(
action, action,
current_user, current_user,
@commit, @commit,
into, @target_branch,
tree_id,
start_project: @start_project, start_project: @start_project,
start_branch_name: @start_branch) start_branch_name: @start_branch)
success success
else rescue Repository::CreateTreeError
error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically. error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically.
A #{action.to_s.dasherize} may have already been performed with this #{@commit.change_type_title(current_user)}, or a more recent commit may have updated some of its content." A #{action.to_s.dasherize} may have already been performed with this #{@commit.change_type_title(current_user)}, or a more recent commit may have updated some of its content."
raise ChangeError, error_msg raise ChangeError, error_msg
end end
end
def check_push_permissions def check_push_permissions
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch) allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
...@@ -66,16 +53,17 @@ module Commits ...@@ -66,16 +53,17 @@ module Commits
true true
end end
def validate_target_branch(new_branch) def validate_target_branch
# Temporary branch exists and contains the change commit
return if repository.find_branch(new_branch)
result = ValidateNewBranchService.new(@project, current_user) result = ValidateNewBranchService.new(@project, current_user)
.execute(new_branch) .execute(@target_branch)
if result[:status] == :error if result[:status] == :error
raise ChangeError, "There was an error creating the source branch: #{result[:message]}" raise ChangeError, "There was an error creating the source branch: #{result[:message]}"
end end
end end
def different_branch?
@start_branch != @target_branch || @start_project != @project
end
end end
end end
...@@ -62,16 +62,12 @@ module Files ...@@ -62,16 +62,12 @@ module Files
raise_error("You are not allowed to push into this branch") raise_error("You are not allowed to push into this branch")
end end
unless project.empty_repo? if !@start_project.empty_repo? && !@start_project.repository.branch_exists?(@start_branch)
unless @start_project.repository.branch_exists?(@start_branch) raise ValidationError, 'You can only create or edit files when you are on a branch'
raise_error('You can only create or edit files when you are on a branch')
end end
if different_branch? if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name)
if repository.branch_exists?(@target_branch) raise ValidationError, "A branch called #{@branch_name} already exists. Switch to that branch in order to make changes"
raise_error('Branch with such name already exists. You need to switch to this branch in order to make changes')
end
end
end end
end end
......
...@@ -56,12 +56,16 @@ class GitOperationService ...@@ -56,12 +56,16 @@ class GitOperationService
start_project: repository.project, start_project: repository.project,
&block) &block)
check_with_branch_arguments!( start_repository = start_project.repository
branch_name, start_branch_name, start_project) start_branch_name = nil if start_repository.empty_repo?
if start_branch_name && !start_repository.branch_exists?(start_branch_name)
raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}"
end
update_branch_with_hooks(branch_name) do update_branch_with_hooks(branch_name) do
repository.with_repo_branch_commit( repository.with_repo_branch_commit(
start_project.repository, start_repository,
start_branch_name || branch_name, start_branch_name || branch_name,
&block) &block)
end end
...@@ -149,31 +153,4 @@ class GitOperationService ...@@ -149,31 +153,4 @@ class GitOperationService
repository.raw_repository.autocrlf = :input repository.raw_repository.autocrlf = :input
end end
end end
def check_with_branch_arguments!(
branch_name, start_branch_name, start_project)
return if repository.branch_exists?(branch_name)
if repository.project != start_project
unless start_branch_name
raise ArgumentError,
'Should also pass :start_branch_name if' +
' :start_project is different from current project'
end
unless start_project.repository.branch_exists?(start_branch_name)
raise ArgumentError,
"Cannot find branch #{branch_name} nor" \
" #{start_branch_name} from" \
" #{start_project.path_with_namespace}"
end
elsif start_branch_name
unless repository.branch_exists?(start_branch_name)
raise ArgumentError,
"Cannot find branch #{branch_name} nor" \
" #{start_branch_name} from" \
" #{repository.project.path_with_namespace}"
end
end
end
end end
...@@ -385,7 +385,6 @@ module SystemNoteService ...@@ -385,7 +385,6 @@ module SystemNoteService
# Returns Boolean # Returns Boolean
def cross_reference_disallowed?(noteable, mentioner) def cross_reference_disallowed?(noteable, mentioner)
return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active? return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
return true if noteable.is_a?(Issuable) && (noteable.try(:closed?) || noteable.try(:merged?))
return false unless mentioner.is_a?(MergeRequest) return false unless mentioner.is_a?(MergeRequest)
return false unless noteable.is_a?(Commit) return false unless noteable.is_a?(Commit)
......
.js-projects-list-holder
- if @projects.any?
%ul.projects-list.content-list
- @projects.each_with_index do |project|
%li.project-row
.controls
- if project.archived
%span.label.label-warning archived
%span.badge
= storage_counter(project.statistics.storage_size)
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
= link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
.title
= link_to [:admin, project.namespace.becomes(Namespace), project] do
.dash-project-avatar
.avatar-container.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
%span.project-full-name
%span.namespace-name
- if project.namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
= project.name
- if project.description.present?
.description
= markdown_field(project, :description)
= paginate @projects, theme: 'gitlab'
- else
.nothing-here-block No projects found
...@@ -7,28 +7,12 @@ ...@@ -7,28 +7,12 @@
%div{ class: container_class } %div{ class: container_class }
.top-area .top-area
.prepend-top-default .prepend-top-default
= form_tag admin_projects_path, method: :get do |f|
.search-holder .search-holder
.search-field-holder = render 'shared/projects/search_form', autofocus: true, icon: true
= search_field_tag :name, params[:name], class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false, placeholder: 'Search by name'
- if params[:visibility_level].present?
= hidden_field_tag 'visibility_level', params[:visibility_level]
- if params[:sort].present?
= hidden_field_tag 'sort', params[:sort]
- if params[:personal].present?
= hidden_field_tag 'visibility_level', 'true'
- if params[:archived].present?
= hidden_field_tag 'archived', 'true'
= icon("search", class: "search-icon")
.dropdown .dropdown
- toggle_text = 'Namespace' - toggle_text = 'Namespace'
- if params[:namespace_id].present? - if params[:namespace_id].present?
= hidden_field_tag :namespace_id, params[:namespace_id]
- namespace = Namespace.find(params[:namespace_id]) - namespace = Namespace.find(params[:namespace_id])
- toggle_text = "#{namespace.kind}: #{namespace.full_path}" - toggle_text = "#{namespace.kind}: #{namespace.full_path}"
= dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' }) = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' })
...@@ -58,35 +42,4 @@ ...@@ -58,35 +42,4 @@
= link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do
Public Public
.projects-list-holder = render 'projects'
- if @projects.any?
%ul.projects-list.content-list
- @projects.each_with_index do |project|
%li.project-row
.controls
- if project.archived
%span.label.label-warning archived
%span.badge
= storage_counter(project.statistics.storage_size)
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
= link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
.title
= link_to [:admin, project.namespace.becomes(Namespace), project] do
.dash-project-avatar
.avatar-container.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
%span.project-full-name
%span.namespace-name
- if project.namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
= project.name
- if project.description.present?
.description
= markdown_field(project, :description)
= paginate @projects, theme: 'gitlab'
- else
.nothing-here-block No projects found
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
.nav-block .nav-block
- if current_user
.controls .controls
= link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do = link_to dashboard_projects_path(rss_url_options), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
%i.fa.fa-rss %i.fa.fa-rss
= render 'shared/event_filter' = render 'shared/event_filter'
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
= nav_link(page: explore_groups_path) do = nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore groups' do = link_to explore_groups_path, title: 'Explore groups' do
Explore Groups Explore Groups
- if current_user.can_create_group?
.nav-controls .nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
- if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new" do = link_to new_group_path, class: "btn btn-new" do
New Group New Group
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
Explore projects Explore projects
.nav-controls .nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = render 'shared/projects/search_form'
= search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2"
= render 'shared/projects/dropdown' = render 'shared/projects/dropdown'
- if current_user.can_create_project? - if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do = link_to new_project_path, class: 'btn btn-new' do
......
= content_for :meta_tags do = content_for :meta_tags do
- if current_user = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
- page_title "Activity" - page_title "Activity"
- header_title "Activity", activity_dashboard_path - header_title "Activity", activity_dashboard_path
......
.js-groups-list-holder
%ul.content-list
- @group_members.each do |group_member|
= render 'shared/groups/group', group: group_member.group, group_member: group_member
= paginate @group_members, theme: 'gitlab'
...@@ -5,9 +5,4 @@ ...@@ -5,9 +5,4 @@
- if @group_members.empty? - if @group_members.empty?
= render 'empty_state' = render 'empty_state'
- else - else
%ul.content-list = render 'groups'
- @group_members.each do |group_member|
- group = group_member.group
= render 'shared/groups/group', group: group, group_member: group_member
= paginate @group_members, theme: 'gitlab'
- page_title "Issues" - page_title "Issues"
- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id) - header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do = content_for :meta_tags do
- if current_user = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues")
= auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{current_user.name} issues")
.top-area .top-area
= render 'shared/issuable/nav', type: :issues = render 'shared/issuable/nav', type: :issues
.nav-controls .nav-controls
- if current_user = link_to params.merge(rss_url_options), class: 'btn' do
= link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
= icon('rss') = icon('rss')
%span.icon-label %span.icon-label
Subscribe Subscribe
......
xml.instruct! xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Activity" xml.title "Activity"
xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: dashboard_projects_url(rss_url_options), rel: "self", type: "application/atom+xml"
xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html" xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html"
xml.id dashboard_projects_url xml.id dashboard_projects_url
xml.updated @events[0].updated_at.xmlschema if @events[0] xml.updated @events[0].updated_at.xmlschema if @events[0]
......
= content_for :meta_tags do = content_for :meta_tags do
- if current_user = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
.user-callout{ 'callout-svg' => custom_icon('icon_customization') } .user-callout{ 'callout-svg' => custom_icon('icon_customization') }
- if @projects.any? || params[:name]
- if @projects.any? || params[:filter_projects]
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- if @last_push - if @last_push
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
- if @projects.any? || params[:filter_projects] - if @projects.any? || params[:name]
= render 'projects' = render 'projects'
- else - else
= render "zero_authorized_projects" = render "zero_authorized_projects"
.js-groups-list-holder
%ul.content-list
- @groups.each do |group|
= render 'shared/groups/group', group: group
= paginate @groups, theme: 'gitlab'
.top-area
%ul.nav-links
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path do
Explore Groups
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
...@@ -5,41 +5,11 @@ ...@@ -5,41 +5,11 @@
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
- else - else
= render 'explore/head' = render 'explore/head'
= render 'nav'
.row-content-block.clearfix - if @groups.present?
.pull-left = render 'groups'
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| - else
= hidden_field_tag :sort, @sort
.form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search", spellcheck: false
.form-group
= button_tag 'Search', class: "btn btn-default"
.pull-right
.dropdown.inline
%button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to explore_groups_path(sort: sort_value_recently_created) do
= sort_title_recently_created
= link_to explore_groups_path(sort: sort_value_oldest_created) do
= sort_title_oldest_created
= link_to explore_groups_path(sort: sort_value_recently_updated) do
= sort_title_recently_updated
= link_to explore_groups_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
%ul.content-list
- @groups.each do |group|
= render 'shared/groups/group', group: group
- unless @groups.present?
.nothing-here-block No public groups .nothing-here-block No public groups
= paginate @groups, theme: "gitlab" = paginate @groups, theme: "gitlab"
%ul.nav-links .top-area
%ul.nav-links
= nav_link(page: [trending_explore_projects_path, explore_root_path]) do = nav_link(page: [trending_explore_projects_path, explore_root_path]) do
= link_to trending_explore_projects_path do = link_to trending_explore_projects_path do
Trending Trending
...@@ -8,3 +9,9 @@ ...@@ -8,3 +9,9 @@
= nav_link(page: explore_projects_path) do = nav_link(page: explore_projects_path) do
= link_to explore_projects_path do = link_to explore_projects_path do
All All
.nav-controls
- unless current_user
= render 'shared/projects/search_form'
= render 'shared/projects/dropdown'
= render 'filter'
...@@ -6,10 +6,5 @@ ...@@ -6,10 +6,5 @@
- else - else
= render 'explore/head' = render 'explore/head'
.top-area = render 'explore/projects/nav'
= render 'explore/projects/nav'
.nav-controls
= render 'filter'
= render 'projects', projects: @projects = render 'projects', projects: @projects
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
.nav-block .nav-block
- if current_user
.controls .controls
= link_to group_path(@group, format: :atom, private_token: current_user.private_token), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do = link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
%i.fa.fa-rss %i.fa.fa-rss
= render 'shared/event_filter' = render 'shared/event_filter'
......
= content_for :meta_tags do = content_for :meta_tags do
- if current_user = auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
- page_title "Activity" - page_title "Activity"
= render 'groups/head' = render 'groups/head'
......
- page_title "Issues" - page_title "Issues"
= render "head_issues" = render "head_issues"
= content_for :meta_tags do = content_for :meta_tags do
- if current_user = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
= auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues")
- if group_issues(@group).exists? - if group_issues(@group).exists?
.top-area .top-area
= render 'shared/issuable/nav', type: :issues = render 'shared/issuable/nav', type: :issues
- if current_user
.nav-controls .nav-controls
= link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do = link_to params.merge(rss_url_options), class: 'btn' do
= icon('rss') = icon('rss')
%span.icon-label %span.icon-label
Subscribe Subscribe
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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