Commit 8a928af0 authored by Simon Knox's avatar Simon Knox

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into task_list_refactor

parents 3f713db0 b05e75b8
...@@ -12,12 +12,18 @@ ...@@ -12,12 +12,18 @@
"localStorage": false "localStorage": false
}, },
"plugins": [ "plugins": [
"filenames" "filenames",
"import"
], ],
"settings": {
"import/resolver": {
"webpack": {
"config": "./config/webpack.config.js"
}
}
},
"rules": { "rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"], "filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
"no-multiple-empty-lines": ["error", { "max": 1 }], "no-multiple-empty-lines": ["error", { "max": 1 }]
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off"
} }
} }
...@@ -107,7 +107,10 @@ setup-test-env: ...@@ -107,7 +107,10 @@ setup-test-env:
<<: *dedicated-runner <<: *dedicated-runner
stage: prepare stage: prepare
script: script:
- npm install - node --version
- yarn --version
- yarn install --pure-lockfile
- yarn check # ensure that yarn.lock matches package.json
- bundle exec rake gitlab:assets:compile - bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts: artifacts:
...@@ -246,13 +249,12 @@ karma: ...@@ -246,13 +249,12 @@ karma:
<<: *use-db <<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
script: script:
- npm link istanbul
- bundle exec rake karma - bundle exec rake karma
artifacts: artifacts:
name: coverage-javascript name: coverage-javascript
expire_in: 31d expire_in: 31d
paths: paths:
- coverage-javascript/default/ - coverage-javascript/
lint-doc: lint-doc:
stage: test stage: test
...@@ -325,11 +327,9 @@ lint:javascript: ...@@ -325,11 +327,9 @@ lint:javascript:
paths: paths:
- node_modules/ - node_modules/
stage: test stage: test
image: "node:7.1" before_script: []
before_script:
- npm install
script: script:
- npm --silent run eslint - yarn run eslint
lint:javascript:report: lint:javascript:report:
<<: *dedicated-runner <<: *dedicated-runner
...@@ -337,12 +337,10 @@ lint:javascript:report: ...@@ -337,12 +337,10 @@ lint:javascript:report:
paths: paths:
- node_modules/ - node_modules/
stage: post-test stage: post-test
image: "node:7.1" before_script: []
before_script:
- npm install
script: script:
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- npm --silent run eslint-report || true # ignore exit code - yarn run eslint-report || true # ignore exit code
artifacts: artifacts:
name: eslint-report name: eslint-report
expire_in: 31d expire_in: 31d
...@@ -395,7 +393,7 @@ pages: ...@@ -395,7 +393,7 @@ pages:
- mv public/ .public/ - mv public/ .public/
- mkdir public/ - mkdir public/
- mv coverage/ public/coverage-ruby/ || true - mv coverage/ public/coverage-ruby/ || true
- mv coverage-javascript/default/ public/coverage-javascript/ || true - mv coverage-javascript/ public/coverage-javascript/ || true
- mv eslint-report.html public/ || true - mv eslint-report.html public/ || true
artifacts: artifacts:
paths: paths:
......
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 8.16.5 (2017-02-14)
- Patch Asciidocs rendering to block XSS.
- Fix XSS vulnerability in SVG attachments.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.16.4 (2017-02-02) ## 8.16.4 (2017-02-02)
- Support non-ASCII characters in GFM autocomplete. !8729 - Support non-ASCII characters in GFM autocomplete. !8729
...@@ -174,6 +181,13 @@ entry. ...@@ -174,6 +181,13 @@ entry.
- Add margin to markdown math blocks. - Add margin to markdown math blocks.
- Add hover state to MR comment reply button. - Add hover state to MR comment reply button.
## 8.15.6 (2017-02-14)
- Patch Asciidocs rendering to block XSS.
- Fix XSS vulnerability in SVG attachments.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.15.4 (2017-01-09) ## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176 - Make successful pipeline emails off for watchers. !8176
...@@ -437,6 +451,13 @@ entry. ...@@ -437,6 +451,13 @@ entry.
- Whitelist next project names: help, ci, admin, search. !8227 - Whitelist next project names: help, ci, admin, search. !8227
- Adds back CSS for progress-bars. !8237 - Adds back CSS for progress-bars. !8237
## 8.14.9 (2017-02-14)
- Patch Asciidocs rendering to block XSS.
- Fix XSS vulnerability in SVG attachments.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.14.8 (2017-01-25) ## 8.14.8 (2017-01-25)
- Accept environment variables from the `pre-receive` script. !7967 - Accept environment variables from the `pre-receive` script. !7967
......
...@@ -29,6 +29,7 @@ gem 'omniauth-github', '~> 1.1.1' ...@@ -29,6 +29,7 @@ gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.2' gem 'omniauth-gitlab', '~> 1.0.2'
gem 'omniauth-google-oauth2', '~> 0.4.1' gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.7.0' gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
......
...@@ -483,6 +483,8 @@ GEM ...@@ -483,6 +483,8 @@ GEM
omniauth-oauth2 (1.3.1) omniauth-oauth2 (1.3.1)
oauth2 (~> 1.0) oauth2 (~> 1.0)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0)
omniauth-saml (1.7.0) omniauth-saml (1.7.0)
omniauth (~> 1.3) omniauth (~> 1.3)
ruby-saml (~> 1.4) ruby-saml (~> 1.4)
...@@ -931,6 +933,7 @@ DEPENDENCIES ...@@ -931,6 +933,7 @@ DEPENDENCIES
omniauth-gitlab (~> 1.0.2) omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.4.1) omniauth-google-oauth2 (~> 0.4.1)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.7.0) omniauth-saml (~> 1.7.0)
omniauth-shibboleth (~> 1.2.0) omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0) omniauth-twitter (~> 1.2.0)
......
...@@ -56,8 +56,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/)); ...@@ -56,8 +56,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/)); requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/)); requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
require('vendor/fuzzaldrin-plus'); require('vendor/fuzzaldrin-plus');
window.ES6Promise = require('vendor/es6-promise.auto'); require('es6-promise').polyfill();
window.ES6Promise.polyfill();
(function () { (function () {
document.addEventListener('beforeunload', function () { document.addEventListener('beforeunload', function () {
......
...@@ -3,5 +3,5 @@ ...@@ -3,5 +3,5 @@
Vue.filter('due-date', (value) => { Vue.filter('due-date', (value) => {
const date = new Date(value); const date = new Date(value);
return dateFormat(date, 'mmm d, yyyy'); return dateFormat(date, 'mmm d, yyyy', true);
}); });
...@@ -20,7 +20,10 @@ $(() => { ...@@ -20,7 +20,10 @@ $(() => {
gl.commits.PipelinesTableBundle.$destroy(true); gl.commits.PipelinesTableBundle.$destroy(true);
} }
gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView({ const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
el: document.querySelector('#commit-pipeline-table-view'), gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView();
});
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
}
}); });
...@@ -8,7 +8,22 @@ ...@@ -8,7 +8,22 @@
* Uses Vue.Resource * Uses Vue.Resource
*/ */
class PipelinesService { class PipelinesService {
constructor(endpoint) {
/**
* FIXME: The url provided to request the pipelines in the new merge request
* page already has `.json`.
* This should be fixed when the endpoint is improved.
*
* @param {String} root
*/
constructor(root) {
let endpoint;
if (root.indexOf('.json') === -1) {
endpoint = `${root}.json`;
} else {
endpoint = root;
}
this.pipelines = Vue.resource(endpoint); this.pipelines = Vue.resource(endpoint);
} }
......
...@@ -56,15 +56,14 @@ require('./pipelines_store'); ...@@ -56,15 +56,14 @@ require('./pipelines_store');
}, },
/** /**
* When the component is created the service to fetch the data will be * When the component is about to be mounted, tell the service to fetch the data
* initialized with the correct endpoint.
* *
* A request to fetch the pipelines will be made. * A request to fetch the pipelines will be made.
* In case of a successfull response we will store the data in the provided * In case of a successfull response we will store the data in the provided
* store, in case of a failed response we need to warn the user. * store, in case of a failed response we need to warn the user.
* *
*/ */
created() { beforeMount() {
const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint); const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint);
this.isLoading = true; this.isLoading = true;
...@@ -82,8 +81,8 @@ require('./pipelines_store'); ...@@ -82,8 +81,8 @@ require('./pipelines_store');
}, },
template: ` template: `
<div> <div class="pipelines">
<div class="pipelines realtime-loading" v-if="isLoading"> <div class="realtime-loading" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i> <i class="fa fa-spinner fa-spin"></i>
</div> </div>
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $dueDateInput.get(0), field: $dueDateInput.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'YYYY-MM-DD', format: 'yyyy-mm-dd',
onSelect: (dateText) => { onSelect: (dateText) => {
const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd');
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
} }
}); });
calendar.setDate(new Date($dueDateInput.val()));
this.$datePicker.append(calendar.el); this.$datePicker.append(calendar.el);
this.$datePicker.data('pikaday', calendar); this.$datePicker.data('pikaday', calendar);
} }
...@@ -169,11 +170,12 @@ ...@@ -169,11 +170,12 @@
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $datePicker.get(0), field: $datePicker.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'YYYY-MM-DD', format: 'yyyy-mm-dd',
onSelect(dateText) { onSelect(dateText) {
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
} }
}); });
calendar.setDate(new Date($datePicker.val()));
$datePicker.data('pikaday', calendar); $datePicker.data('pikaday', calendar);
}); });
......
...@@ -47,9 +47,11 @@ ...@@ -47,9 +47,11 @@
} }
// Only filter asynchronously only if option remote is set // Only filter asynchronously only if option remote is set
if (this.options.remote) { if (this.options.remote) {
$inputContainer.parent().addClass('is-loading');
clearTimeout(timeout); clearTimeout(timeout);
return timeout = setTimeout(function() { return timeout = setTimeout(function() {
return this.options.query(this.input.val(), function(data) { return this.options.query(this.input.val(), function(data) {
$inputContainer.parent().removeClass('is-loading');
return this.options.callback(data); return this.options.callback(data);
}.bind(this)); }.bind(this));
}.bind(this), 250); }.bind(this), 250);
......
...@@ -40,11 +40,12 @@ ...@@ -40,11 +40,12 @@
calendar = new Pikaday({ calendar = new Pikaday({
field: $issuableDueDate.get(0), field: $issuableDueDate.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'YYYY-MM-DD', format: 'yyyy-mm-dd',
onSelect: function(dateText) { onSelect: function(dateText) {
$issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
} }
}); });
calendar.setDate(new Date($issuableDueDate.val()));
} }
} }
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $input.get(0), field: $input.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'YYYY-MM-DD', format: 'yyyy-mm-dd',
minDate: new Date(), minDate: new Date(),
onSelect(dateText) { onSelect(dateText) {
$input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
}, },
}); });
calendar.setDate(new Date($input.val()));
$input.data('pikaday', calendar); $input.data('pikaday', calendar);
}); });
......
...@@ -61,6 +61,7 @@ require('./flash'); ...@@ -61,6 +61,7 @@ require('./flash');
constructor({ action, setUrl, stubLocation } = {}) { constructor({ action, setUrl, stubLocation } = {}) {
this.diffsLoaded = false; this.diffsLoaded = false;
this.pipelinesLoaded = false;
this.commitsLoaded = false; this.commitsLoaded = false;
this.fixedLayoutPref = null; this.fixedLayoutPref = null;
...@@ -102,9 +103,10 @@ require('./flash'); ...@@ -102,9 +103,10 @@ require('./flash');
} }
clickTab(e) { clickTab(e) {
if (e.target && gl.utils.isMetaClick(e)) { if (e.currentTarget && gl.utils.isMetaClick(e)) {
const targetLink = e.target.getAttribute('href'); const targetLink = e.currentTarget.getAttribute('href');
e.stopImmediatePropagation(); e.stopImmediatePropagation();
e.preventDefault();
window.open(targetLink, '_blank'); window.open(targetLink, '_blank');
} }
} }
...@@ -128,6 +130,13 @@ require('./flash'); ...@@ -128,6 +130,13 @@ require('./flash');
$.scrollTo('.merge-request-details .merge-request-tabs', { $.scrollTo('.merge-request-details .merge-request-tabs', {
offset: 0, offset: 0,
}); });
} else if (action === 'pipelines') {
if (this.pipelinesLoaded) {
return;
}
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl);
this.pipelinesLoaded = true;
} else { } else {
this.expandView(); this.expandView();
this.resetViewContainer(); this.resetViewContainer();
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
if (selected.id == null) { if (selected.id == null) {
return selected.text; return selected.text;
} else { } else {
return selected.kind + ": " + selected.path; return selected.kind + ": " + selected.full_path;
} }
}, },
data: function(term, dataCallback) { data: function(term, dataCallback) {
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
if (namespace.id == null) { if (namespace.id == null) {
return namespace.text; return namespace.text;
} else { } else {
return namespace.kind + ": " + namespace.path; return namespace.kind + ": " + namespace.full_path;
} }
}, },
renderRow: this.renderRow, renderRow: this.renderRow,
......
...@@ -905,9 +905,10 @@ require('./task_list'); ...@@ -905,9 +905,10 @@ require('./task_list');
}; };
Notes.prototype.toggleCommitList = function(e) { Notes.prototype.toggleCommitList = function(e) {
const $element = $(e.target); const $element = $(e.currentTarget);
const $closestSystemCommitList = $element.siblings('.system-note-commit-list'); const $closestSystemCommitList = $element.siblings('.system-note-commit-list');
$element.find('.fa').toggleClass('fa-angle-down').toggleClass('fa-angle-up');
$closestSystemCommitList.toggleClass('hide-shade'); $closestSystemCommitList.toggleClass('hide-shade');
}; };
......
...@@ -147,24 +147,21 @@ ...@@ -147,24 +147,21 @@
goToTodoUrl(e) { goToTodoUrl(e) {
const todoLink = this.dataset.url; const todoLink = this.dataset.url;
let targetLink = e.target.getAttribute('href');
if (e.target.tagName === 'IMG') { // See if clicked target was Avatar
targetLink = e.target.parentElement.getAttribute('href'); // Parent of Avatar is link
}
if (!todoLink) { if (!todoLink) {
return; return;
} }
if (gl.utils.isMetaClick(e)) { if (gl.utils.isMetaClick(e)) {
const windowTarget = '_blank';
const selected = e.target;
e.preventDefault(); e.preventDefault();
// Meta-Click on username leads to different URL than todoLink.
// Turbolinks can resolve that URL, but window.open requires URL manually. if (selected.tagName === 'IMG') {
if (targetLink !== todoLink) { const avatarUrl = selected.parentElement.getAttribute('href');
return window.open(targetLink, '_blank'); return window.open(avatarUrl, windowTarget);
} else { } else {
return window.open(todoLink, '_blank'); return window.open(todoLink, windowTarget);
} }
} else { } else {
return gl.utils.visitUrl(todoLink); return gl.utils.visitUrl(todoLink);
......
...@@ -193,7 +193,6 @@ ...@@ -193,7 +193,6 @@
top: $header-height; top: $header-height;
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 8;
transition: width .3s; transition: width .3s;
background: $gray-light; background: $gray-light;
padding: 10px 20px; padding: 10px 20px;
......
...@@ -72,6 +72,7 @@ ul.notes { ...@@ -72,6 +72,7 @@ ul.notes {
overflow: hidden; overflow: hidden;
.system-note-commit-list-toggler { .system-note-commit-list-toggler {
color: $gl-link-color;
display: none; display: none;
padding: 10px 0 0; padding: 10px 0 0;
cursor: pointer; cursor: pointer;
...@@ -107,16 +108,6 @@ ul.notes { ...@@ -107,16 +108,6 @@ ul.notes {
display: none; display: none;
} }
p:last-child {
a {
color: $gl-text-color;
&:hover {
color: $gl-link-color;
}
}
}
&::after { &::after {
content: ''; content: '';
width: 100%; width: 100%;
......
...@@ -864,7 +864,7 @@ ...@@ -864,7 +864,7 @@
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
width: 90px; max-width: 70%;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
margin-left: 2px; margin-left: 2px;
display: inline-block; display: inline-block;
......
...@@ -35,12 +35,8 @@ ...@@ -35,12 +35,8 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
.project-path { .project-path .form-control {
padding-right: 0; border-radius: $border-radius-base;
.form-control {
border-radius: $border-radius-base;
}
} }
.input-group > div { .input-group > div {
......
...@@ -171,6 +171,8 @@ ...@@ -171,6 +171,8 @@
.tree-controls { .tree-controls {
float: right; float: right;
margin-top: 11px; margin-top: 11px;
position: relative;
z-index: 2;
.project-action-button { .project-action-button {
margin-left: $btn-side-margin; margin-left: $btn-side-margin;
......
...@@ -27,7 +27,7 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -27,7 +27,7 @@ class Projects::TagsController < Projects::ApplicationController
end end
def create def create
result = CreateTagService.new(@project, current_user). result = Tags::CreateService.new(@project, current_user).
execute(params[:tag_name], params[:ref], params[:message], params[:release_description]) execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
if result[:status] == :success if result[:status] == :success
...@@ -41,7 +41,7 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -41,7 +41,7 @@ class Projects::TagsController < Projects::ApplicationController
end end
def destroy def destroy
DeleteTagService.new(project, current_user).execute(params[:id]) Tags::DestroyService.new(project, current_user).execute(params[:id])
respond_to do |format| respond_to do |format|
format.html do format.html do
......
...@@ -84,7 +84,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -84,7 +84,7 @@ class Projects::WikisController < Projects::ApplicationController
def destroy def destroy
@page = @project_wiki.find_page(params[:id]) @page = @project_wiki.find_page(params[:id])
@page&.delete WikiPages::DestroyService.new(@project, current_user).execute(@page)
redirect_to( redirect_to(
namespace_project_wiki_path(@project.namespace, @project, :home), namespace_project_wiki_path(@project.namespace, @project, :home),
......
...@@ -75,10 +75,10 @@ module MergeRequestsHelper ...@@ -75,10 +75,10 @@ module MergeRequestsHelper
new_namespace_project_merge_request_path( new_namespace_project_merge_request_path(
@project.namespace, @project, @project.namespace, @project,
merge_request: { merge_request: {
source_project_id: @merge_request.source_project_id, source_project_id: merge_request.source_project_id,
target_project_id: @merge_request.target_project_id, target_project_id: merge_request.target_project_id,
source_branch: @merge_request.source_branch, source_branch: merge_request.source_branch,
target_branch: @merge_request.target_branch, target_branch: merge_request.target_branch,
}, },
change_branches: true change_branches: true
) )
......
...@@ -10,7 +10,7 @@ module NamespacesHelper ...@@ -10,7 +10,7 @@ module NamespacesHelper
data_attr_users = { 'data-options-parent' => 'users' } data_attr_users = { 'data-options-parent' => 'users' }
group_opts = [ group_opts = [
"Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.path : g.human_name, g.id, data_attr_group] } "Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.full_path : g.human_name, g.id, data_attr_group] }
] ]
users_opts = [ users_opts = [
......
...@@ -63,7 +63,7 @@ module SubmoduleHelper ...@@ -63,7 +63,7 @@ module SubmoduleHelper
namespace = components.pop.gsub(/^\.\.$/, '') namespace = components.pop.gsub(/^\.\.$/, '')
if namespace.empty? if namespace.empty?
namespace = @project.namespace.path namespace = @project.namespace.full_path
end end
[ [
......
...@@ -45,9 +45,10 @@ class Event < ActiveRecord::Base ...@@ -45,9 +45,10 @@ class Event < ActiveRecord::Base
class << self class << self
# Update Gitlab::ContributionsCalendar#activity_dates if this changes # Update Gitlab::ContributionsCalendar#activity_dates if this changes
def contributions def contributions
where("action = ? OR (target_type in (?) AND action in (?))", where("action = ? OR (target_type IN (?) AND action IN (?)) OR (target_type = ? AND action = ?)",
Event::PUSHED, ["MergeRequest", "Issue"], Event::PUSHED,
[Event::CREATED, Event::CLOSED, Event::MERGED]) ["MergeRequest", "Issue"], [Event::CREATED, Event::CLOSED, Event::MERGED],
"Note", Event::COMMENTED)
end end
def limit_recent(limit = 20, offset = nil) def limit_recent(limit = 20, offset = nil)
......
...@@ -81,7 +81,7 @@ class Group < Namespace ...@@ -81,7 +81,7 @@ class Group < Namespace
end end
def to_reference(_from_project = nil, full: nil) def to_reference(_from_project = nil, full: nil)
"#{self.class.reference_prefix}#{name}" "#{self.class.reference_prefix}#{full_path}"
end end
def web_url def web_url
......
...@@ -598,7 +598,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -598,7 +598,7 @@ class MergeRequest < ActiveRecord::Base
def source_project_namespace def source_project_namespace
if source_project && source_project.namespace if source_project && source_project.namespace
source_project.namespace.path source_project.namespace.full_path
else else
"(removed)" "(removed)"
end end
...@@ -606,7 +606,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -606,7 +606,7 @@ class MergeRequest < ActiveRecord::Base
def target_project_namespace def target_project_namespace
if target_project && target_project.namespace if target_project && target_project.namespace
target_project.namespace.path target_project.namespace.full_path
else else
"(removed)" "(removed)"
end end
......
...@@ -454,7 +454,7 @@ class Project < ActiveRecord::Base ...@@ -454,7 +454,7 @@ class Project < ActiveRecord::Base
if forked? if forked?
job_id = RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, job_id = RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
forked_from_project.path_with_namespace, forked_from_project.path_with_namespace,
self.namespace.path) self.namespace.full_path)
else else
job_id = RepositoryImportWorker.perform_async(self.id) job_id = RepositoryImportWorker.perform_async(self.id)
end end
...@@ -942,8 +942,8 @@ class Project < ActiveRecord::Base ...@@ -942,8 +942,8 @@ class Project < ActiveRecord::Base
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.path) Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
end end
# Expires various caches before a project is renamed. # Expires various caches before a project is renamed.
...@@ -1150,19 +1150,25 @@ class Project < ActiveRecord::Base ...@@ -1150,19 +1150,25 @@ class Project < ActiveRecord::Base
end end
def pages_url def pages_url
subdomain, _, url_path = full_path.partition('/')
# The hostname always needs to be in downcased # The hostname always needs to be in downcased
# All web servers convert hostname to lowercase # All web servers convert hostname to lowercase
host = "#{namespace.path}.#{Settings.pages.host}".downcase host = "#{subdomain}.#{Settings.pages.host}".downcase
# The host in URL always needs to be downcased # The host in URL always needs to be downcased
url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix| url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix|
"#{prefix}#{namespace.path}." "#{prefix}#{subdomain}."
end.downcase end.downcase
# If the project path is the same as host, we serve it as group page # If the project path is the same as host, we serve it as group page
return url if host == path return url if host == url_path
"#{url}/#{url_path}"
end
"#{url}/#{path}" def pages_subdomain
full_path.partition('/').first
end end
def pages_path def pages_path
...@@ -1179,8 +1185,8 @@ class Project < ActiveRecord::Base ...@@ -1179,8 +1185,8 @@ class Project < ActiveRecord::Base
# 3. We asynchronously remove pages with force # 3. We asynchronously remove pages with force
temp_path = "#{path}.#{SecureRandom.hex}.deleted" temp_path = "#{path}.#{SecureRandom.hex}.deleted"
if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path) if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.full_path)
PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path) PagesWorker.perform_in(5.minutes, :remove, namespace.full_path, temp_path)
end end
end end
...@@ -1230,7 +1236,7 @@ class Project < ActiveRecord::Base ...@@ -1230,7 +1236,7 @@ class Project < ActiveRecord::Base
end end
def ensure_dir_exist def ensure_dir_exist
gitlab_shell.add_namespace(repository_storage_path, namespace.path) gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
end end
def predefined_variables def predefined_variables
...@@ -1238,7 +1244,7 @@ class Project < ActiveRecord::Base ...@@ -1238,7 +1244,7 @@ class Project < ActiveRecord::Base
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true }, { key: 'CI_PROJECT_ID', value: id.to_s, public: true },
{ key: 'CI_PROJECT_NAME', value: path, public: true }, { key: 'CI_PROJECT_NAME', value: path, public: true },
{ key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true }, { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: web_url, public: true } { key: 'CI_PROJECT_URL', value: web_url, public: true }
] ]
end end
......
...@@ -12,7 +12,7 @@ class DroneCiService < CiService ...@@ -12,7 +12,7 @@ class DroneCiService < CiService
def compose_service_hook def compose_service_hook
hook = service_hook || build_service_hook hook = service_hook || build_service_hook
# If using a service template, project may not be available # If using a service template, project may not be available
hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.full_path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
hook.enable_ssl_verification = !!enable_ssl_verification hook.enable_ssl_verification = !!enable_ssl_verification
hook.save hook.save
end end
...@@ -38,7 +38,7 @@ class DroneCiService < CiService ...@@ -38,7 +38,7 @@ class DroneCiService < CiService
def commit_status_path(sha, ref) def commit_status_path(sha, ref)
url = [drone_url, url = [drone_url,
"gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}", "gitlab/#{project.full_path}/commits/#{sha}",
"?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"] "?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"]
URI.join(*url).to_s URI.join(*url).to_s
...@@ -73,7 +73,7 @@ class DroneCiService < CiService ...@@ -73,7 +73,7 @@ class DroneCiService < CiService
def build_page(sha, ref) def build_page(sha, ref)
url = [drone_url, url = [drone_url,
"gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}", "gitlab/#{project.full_path}/redirect/commits/#{sha}",
"?branch=#{URI::encode(ref.to_s)}"] "?branch=#{URI::encode(ref.to_s)}"]
URI.join(*url).to_s URI.join(*url).to_s
......
# TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed
class GitlabCiService < CiService
# We override the active accessor to always make GitLabCiService disabled
# Otherwise the GitLabCiService can be picked, but should never be since it's deprecated
def active
false
end
end
...@@ -27,7 +27,7 @@ class Service < ActiveRecord::Base ...@@ -27,7 +27,7 @@ class Service < ActiveRecord::Base
validates :project_id, presence: true, unless: Proc.new { |service| service.template? } validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
scope :issue_trackers, -> { where(category: 'issue_tracker') } scope :issue_trackers, -> { where(category: 'issue_tracker') }
scope :external_wikis, -> { where(type: 'ExternalWikiService').active } scope :external_wikis, -> { where(type: 'ExternalWikiService').active }
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
......
...@@ -335,7 +335,7 @@ class User < ActiveRecord::Base ...@@ -335,7 +335,7 @@ class User < ActiveRecord::Base
def reference_pattern def reference_pattern
%r{ %r{
#{Regexp.escape(reference_prefix)} #{Regexp.escape(reference_prefix)}
(?<user>#{Gitlab::Regex::NAMESPACE_REGEX_STR}) (?<user>#{Gitlab::Regex::NAMESPACE_REF_REGEX_STR})
}x }x
end end
end end
......
...@@ -207,6 +207,10 @@ class WikiPage ...@@ -207,6 +207,10 @@ class WikiPage
'projects/wikis/wiki_page' 'projects/wikis/wiki_page'
end end
def id
page.version.to_s
end
private private
def set_attributes def set_attributes
......
class CreateTagService < BaseService
def execute(tag_name, target, message, release_description = nil)
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
return error('Tag name invalid') unless valid_tag
repository = project.repository
message&.strip!
new_tag = nil
begin
new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Rugged::TagError
return error("Tag #{tag_name} already exists")
rescue GitHooksService::PreReceiveError => ex
return error(ex.message)
end
if new_tag
if release_description
CreateReleaseService.new(@project, @current_user).
execute(tag_name, release_description)
end
success.merge(tag: new_tag)
else
error("Target #{target} is invalid")
end
end
end
class DeleteTagService < BaseService
def execute(tag_name)
repository = project.repository
tag = repository.find_tag(tag_name)
unless tag
return error('No such tag', 404)
end
if repository.rm_tag(current_user, tag_name)
release = project.releases.find_by(tag: tag_name)
release&.destroy
push_data = build_push_data(tag)
EventCreateService.new.push(project, current_user, push_data)
project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks)
success('Tag was removed')
else
error('Failed to remove tag')
end
end
def error(message, return_code = 400)
super(message).merge(return_code: return_code)
end
def success(message)
super().merge(message: message)
end
def build_push_data(tag)
Gitlab::DataBuilder::Push.build(
project,
current_user,
tag.dereferenced_target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
[])
end
end
...@@ -36,7 +36,7 @@ module Projects ...@@ -36,7 +36,7 @@ module Projects
def groups def groups
current_user.authorized_groups.sort_by(&:path).map do |group| current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count count = group.users.count
{ username: group.path, name: group.name, count: count, avatar_url: group.avatar_url } { username: group.full_path, name: group.full_name, count: count, avatar_url: group.avatar_url }
end end
end end
......
...@@ -30,7 +30,7 @@ module Projects ...@@ -30,7 +30,7 @@ module Projects
Project.transaction do Project.transaction do
old_path = project.path_with_namespace old_path = project.path_with_namespace
old_group = project.group old_group = project.group
new_path = File.join(new_namespace.try(:path) || '', project.path) new_path = File.join(new_namespace.try(:full_path) || '', project.path)
if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
raise TransferError.new("Project with same path in target namespace already exists") raise TransferError.new("Project with same path in target namespace already exists")
...@@ -63,10 +63,10 @@ module Projects ...@@ -63,10 +63,10 @@ module Projects
Labels::TransferService.new(current_user, old_group, project).execute Labels::TransferService.new(current_user, old_group, project).execute
# Move uploads # Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path)
# Move pages # Move pages
Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path)
project.old_path_with_namespace = old_path project.old_path_with_namespace = old_path
......
module Tags
class CreateService < BaseService
def execute(tag_name, target, message, release_description = nil)
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
return error('Tag name invalid') unless valid_tag
repository = project.repository
message&.strip!
new_tag = nil
begin
new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Rugged::TagError
return error("Tag #{tag_name} already exists")
rescue GitHooksService::PreReceiveError => ex
return error(ex.message)
end
if new_tag
if release_description
CreateReleaseService.new(@project, @current_user).
execute(tag_name, release_description)
end
success.merge(tag: new_tag)
else
error("Target #{target} is invalid")
end
end
end
end
module Tags
class DestroyService < BaseService
def execute(tag_name)
repository = project.repository
tag = repository.find_tag(tag_name)
unless tag
return error('No such tag', 404)
end
if repository.rm_tag(current_user, tag_name)
release = project.releases.find_by(tag: tag_name)
release&.destroy
push_data = build_push_data(tag)
EventCreateService.new.push(project, current_user, push_data)
project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks)
success('Tag was removed')
else
error('Failed to remove tag')
end
end
def error(message, return_code = 400)
super(message).merge(return_code: return_code)
end
def success(message)
super().merge(message: message)
end
def build_push_data(tag)
Gitlab::DataBuilder::Push.build(
project,
current_user,
tag.dereferenced_target.sha,
Gitlab::Git::BLANK_SHA,
"#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}",
[])
end
end
end
module WikiPages
class DestroyService < WikiPages::BaseService
def execute(page)
if page&.delete
execute_hooks(page, 'delete')
end
page
end
end
end
...@@ -36,7 +36,7 @@ class FileUploader < GitlabUploader ...@@ -36,7 +36,7 @@ class FileUploader < GitlabUploader
escaped_filename = filename.gsub("]", "\\]") escaped_filename = filename.gsub("]", "\\]")
markdown = "[#{escaped_filename}](#{self.secure_url})" markdown = "[#{escaped_filename}](#{self.secure_url})"
markdown.prepend("!") if image_or_video? markdown.prepend("!") if image_or_video? || dangerous?
{ {
alt: filename, alt: filename,
......
# Extra methods for uploader # Extra methods for uploader
module UploaderHelper module UploaderHelper
IMAGE_EXT = %w[png jpg jpeg gif bmp tiff svg] IMAGE_EXT = %w[png jpg jpeg gif bmp tiff]
# We recommend using the .mp4 format over .mov. Videos in .mov format can # We recommend using the .mp4 format over .mov. Videos in .mov format can
# still be used but you really need to make sure they are served with the # still be used but you really need to make sure they are served with the
# proper MIME type video/mp4 and not video/quicktime or your videos won't play # proper MIME type video/mp4 and not video/quicktime or your videos won't play
# on IE >= 9. # on IE >= 9.
# http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html
VIDEO_EXT = %w[mp4 m4v mov webm ogv] VIDEO_EXT = %w[mp4 m4v mov webm ogv]
# These extension types can contain dangerous code and should only be embedded inline with
# proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline".
DANGEROUS_EXT = %w[svg]
def image? def image?
extension_match?(IMAGE_EXT) extension_match?(IMAGE_EXT)
...@@ -20,6 +23,10 @@ module UploaderHelper ...@@ -20,6 +23,10 @@ module UploaderHelper
image? || video? image? || video?
end end
def dangerous?
extension_match?(DANGEROUS_EXT)
end
def extension_match?(extensions) def extension_match?(extensions)
return false unless file return false unless file
......
...@@ -163,6 +163,6 @@ ...@@ -163,6 +163,6 @@
- @groups.each do |group| - @groups.each do |group|
%p %p
= link_to [:admin, group], class: 'str-truncated-60' do = link_to [:admin, group], class: 'str-truncated-60' do
= group.name = group.full_name
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(group.created_at)} #{time_ago_with_tooltip(group.created_at)}
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
- toggle_text = 'Namespace' - toggle_text = 'Namespace'
- if params[:namespace_id].present? - if params[:namespace_id].present?
- namespace = Namespace.find(params[:namespace_id]) - namespace = Namespace.find(params[:namespace_id])
- toggle_text = "#{namespace.kind}: #{namespace.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' })
.dropdown-menu.dropdown-select.dropdown-menu-align-right .dropdown-menu.dropdown-select.dropdown-menu-align-right
= dropdown_title('Namespaces') = dropdown_title('Namespaces')
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
%td %td
#{runner.builds.count(:all)} #{runner.builds.count(:all)}
%td %td
- runner.tag_list.each do |tag| - runner.tag_list.sort.each do |tag|
%span.label.label-primary %span.label.label-primary
= tag = tag
%td %td
......
%ul.nav-links .top-area
%li{ class: ("active" unless params[:filter]) }> %ul.nav-links
= link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do %li{ class: ("active" unless params[:filter]) }>
Your Projects = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do
%li{ class: ("active" if params[:filter] == 'starred') }> Your Projects
= link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do %li{ class: ("active" if params[:filter] == 'starred') }>
Starred Projects = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do
Starred Projects
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import_button = tr.find(".btn-import") import_button = tr.find(".btn-import")
origin_target = target_field.text() origin_target = target_field.text()
project_name = "#{@project_name}" project_name = "#{@project_name}"
origin_namespace = "#{@target_namespace.path}" origin_namespace = "#{@target_namespace.full_path}"
target_field.empty() target_field.empty()
target_field.append("<p class='alert alert-danger'>This namespace has already been taken! Please choose another one.</p>") target_field.append("<p class='alert alert-danger'>This namespace has already been taken! Please choose another one.</p>")
target_field.append("<input type='text' name='target_namespace' />") target_field.append("<input type='text' name='target_namespace' />")
......
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
new Pikaday({ new Pikaday({
field: $dateField.get(0), field: $dateField.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'YYYY-MM-DD', format: 'yyyy-mm-dd',
minDate: new Date(), minDate: new Date(),
onSelect: function(dateText) { onSelect: function(dateText) {
$dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
......
...@@ -28,9 +28,11 @@ ...@@ -28,9 +28,11 @@
.project-clone-holder .project-clone-holder
= render "shared/clone_panel" = render "shared/clone_panel"
- if current_user && can?(current_user, :download_code, @project) - if current_user
= render 'projects/buttons/download', project: @project, ref: @ref - if can?(current_user, :download_code, @project)
= render 'projects/buttons/dropdown' = render 'projects/buttons/download', project: @project, ref: @ref
= render 'projects/buttons/dropdown'
= render 'projects/buttons/koding'
= render 'shared/notifications/button', notification_setting: @notification_setting = render 'shared/notifications/button', notification_setting: @notification_setting
= render 'projects/buttons/koding'
= render 'shared/members/access_request_buttons', source: @project = render 'shared/members/access_request_buttons', source: @project
#commit-pipeline-table-view{ data: { endpoint: endpoint } } - disable_initialization = local_assigns.fetch(:disable_initialization, false)
#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization,
endpoint: endpoint,
} }
.pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"), .pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"),
"icon_status_canceled" => custom_icon("icon_status_canceled"), "icon_status_canceled" => custom_icon("icon_status_canceled"),
"icon_status_running" => custom_icon("icon_status_running"), "icon_status_running" => custom_icon("icon_status_running"),
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.col-lg-9 .col-lg-9
.project-edit-errors .project-edit-errors
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f| = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f|
%fieldset.append-bottom-0 %fieldset
.row .row
.form-group.col-md-9 .form-group.col-md-9
= f.label :name, class: 'label-light', for: 'project_name_edit' do = f.label :name, class: 'label-light', for: 'project_name_edit' do
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
%p.help-block Separate tags with commas. %p.help-block Separate tags with commas.
%hr %hr
%fieldset.append-bottom-0 %fieldset
%h5.prepend-top-0 %h5.prepend-top-0
Sharing &amp; Permissions Sharing &amp; Permissions
.form_group.prepend-top-20.sharing-and-permissions .form_group.prepend-top-20.sharing-and-permissions
...@@ -232,7 +232,7 @@ ...@@ -232,7 +232,7 @@
.form-group .form-group
.input-group .input-group
.input-group-addon .input-group-addon
#{URI.join(root_url, @project.namespace.path)}/ #{URI.join(root_url, @project.namespace.full_path)}/
= f.text_field :path, class: 'form-control' = f.text_field :path, class: 'form-control'
%ul %ul
%li Be careful. Renaming a project's repository can have unintended side effects. %li Be careful. Renaming a project's repository can have unintended side effects.
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
= icon("folder-open-o", class: "settings-list-icon") = icon("folder-open-o", class: "settings-list-icon")
.pull-left .pull-left
= link_to group do = link_to group do
= group.name = group.full_name
%br %br
up to #{group_link.human_access} up to #{group_link.human_access}
- if group_link.expires? - if group_link.expires?
......
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
-# This tab is always loaded via AJAX -# This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane #pipelines.pipelines.tab-pane
- if @pipelines.any? - if @pipelines.any?
= render 'projects/commit/pipelines_list', endpoint: pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
-# This tab is always loaded via AJAX -# This tab is always loaded via AJAX
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
Create or Import your project from popular Git services Create or Import your project from popular Git services
.col-lg-9 .col-lg-9
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
%fieldset.append-bottom-0 .row
.form-group.col-xs-12.col-sm-6 .form-group.col-xs-12.col-sm-6
= f.label :namespace_id, class: 'label-light' do = f.label :namespace_id, class: 'label-light' do
%span %span
......
- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false) - supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
.comment-toolbar.clearfix .comment-toolbar.clearfix
.toolbar-text .toolbar-text
Styling with
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1 = link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
- if supports_slash_commands - if supports_slash_commands
and and
......
...@@ -77,6 +77,7 @@ ...@@ -77,6 +77,7 @@
- if note.system - if note.system
.system-note-commit-list-toggler .system-note-commit-list-toggler
Toggle commit list Toggle commit list
%i.fa.fa-angle-down
- if note.attachment.url - if note.attachment.url
.note-attachment .note-attachment
- if note.attachment.image? - if note.attachment.image?
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
%p %p
To access the domain create a new DNS record: To access the domain create a new DNS record:
%pre %pre
#{@domain.domain} CNAME #{@domain.project.namespace.path}.#{Settings.pages.host}. #{@domain.domain} CNAME #{@domain.project.pages_subdomain}.#{Settings.pages.host}.
%tr %tr
%td %td
Certificate Certificate
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
= label_tag :tag_list, class: 'control-label' do = label_tag :tag_list, class: 'control-label' do
Tags Tags
.col-sm-10 .col-sm-10
= f.text_field :tag_list, value: runner.tag_list.to_s, class: 'form-control' = f.text_field :tag_list, value: runner.tag_list.sort.join(', '), class: 'form-control'
.help-block You can setup jobs to only use Runners with specific tags .help-block You can setup jobs to only use Runners with specific tags
.form-actions .form-actions
= f.submit 'Save changes', class: 'btn btn-save' = f.submit 'Save changes', class: 'btn btn-save'
...@@ -31,6 +31,6 @@ ...@@ -31,6 +31,6 @@
= runner.description = runner.description
- if runner.tag_list.present? - if runner.tag_list.present?
%p %p
- runner.tag_list.each do |tag| - runner.tag_list.sort.each do |tag|
%span.label.label-primary %span.label.label-primary
= tag = tag
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
%tr %tr
%td Tags %td Tags
%td %td
- @runner.tag_list.each do |tag| - @runner.tag_list.sort.each do |tag|
%span.label.label-primary %span.label.label-primary
= tag = tag
%tr %tr
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%span.list-item-name %span.list-item-name
= image_tag group_icon(group), class: "avatar s40", alt: '' = image_tag group_icon(group), class: "avatar s40", alt: ''
%strong %strong
= link_to group.name, group_path(group) = link_to group.full_name, group_path(group)
.cgray .cgray
Joined #{time_ago_with_tooltip(group.created_at)} Joined #{time_ago_with_tooltip(group.created_at)}
- if group_link.expires? - if group_link.expires?
......
.clearfix.calendar .clearfix.calendar
.js-contrib-calendar .js-contrib-calendar
.calendar-hint .calendar-hint
Summary of issues, merge requests, and push events Summary of issues, merge requests, push events, and comments
:javascript :javascript
new Calendar( new Calendar(
#{@activity_dates.to_json}, #{@activity_dates.to_json},
......
...@@ -10,11 +10,17 @@ ...@@ -10,11 +10,17 @@
%i.fa.fa-clock-o %i.fa.fa-clock-o
= event.created_at.to_s(:time) = event.created_at.to_s(:time)
- if event.push? - if event.push?
#{event.action_name} #{event.ref_type} #{event.ref_name} #{event.action_name} #{event.ref_type}
%strong
- commits_path = namespace_project_commits_path(event.project.namespace, event.project, event.ref_name)
= link_to_if event.project.repository.branch_exists?(event.ref_name), event.ref_name, commits_path
- else - else
= event_action_name(event) = event_action_name(event)
- if event.target %strong
%strong= link_to "#{event.target.to_reference}", [event.project.namespace.becomes(Namespace), event.project, event.target] - if event.note?
= link_to event.note_target.to_reference, event_note_target_path(event)
- elsif event.target
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target]
at at
%strong %strong
......
...@@ -106,6 +106,8 @@ ...@@ -106,6 +106,8 @@
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
.user-calendar-activities .user-calendar-activities
%h4.prepend-top-20
Most Recent Activity
.content_list{ data: { href: user_path } } .content_list{ data: { href: user_path } }
= spinner = spinner
......
---
title: Move /projects/fork/:id to /projects/:id/fork
merge_request: 8940
author:
---
title: Execute web hooks for WikiPage delete operation
merge_request: 8198
author:
---
title: Add discussion events to contributions calendar
merge_request: 8821
author:
---
title: 'API: Consolidate /projects endpoint'
merge_request: 8962
author:
---
title: Added 'Most Recent Activity' header to the User Profile page
merge_request: 9189
author: Jan Christophersen
---
title: Add Links to Branches in Calendar Activity
merge_request: 9224
author: Jan Christophersen
---
title: Specify in the documentation that only projects owners can transfer projects
merge_request:
author:
title: Add the oauth2_generic OmniAuth strategy
merge_request: 9048
author: Joe Marty
\ No newline at end of file
---
title: "Fix small height of activity header page"
merge_request: 8952
author: Pavel Sorokin
---
title: Update doc for enabling or disabling GitLab CI
merge_request: 8965
author: Takuya Noguchi
---
title: Fix regression where cmd-click stopped working for todos and merge request
tabs
merge_request:
author:
---
title: Set maximum width for mini pipeline graph text so it is not truncated to early
merge_request: 9188
author:
---
title: Fix stray pipelines API request when showing MR
merge_request:
author:
---
title: Fix Merge request pipelines displays JSON
merge_request:
author:
---
title: Display loading indicator when filtering ref switcher dropdown
merge_request:
author:
---
title: Fix z index issues with sidebar
merge_request:
author:
---
title: Centers loading icon vertically and horizontally in pipelines table in commit
view
merge_request:
author:
---
title: Alphabetically sort tags on runner list
merge_request: 8922
author: blackst0ne
---
title: "Use an entity for RepoBranch commits and enhance RepoCommit"
merge_request: 7138
author: Ben Boeckel
---
title: Reintroduce coverage report for JavaScript
merge_request: 9133
author: winniehell
---
title: Replace static fixture for right_sidebar_spec.js
merge_request: 9211
author: winniehell
---
title: Don't connect in Gitlab::Database.adapter_name
merge_request:
author:
---
title: Show notifications settings dropdown even if repository feature is disabled
merge_request: 9180
author:
---
title: Fix timezone on issue boards due date
merge_request:
author:
---
title: Disable invalid service templates
merge_request:
author:
---
title: Move tag services to Tags namespace
merge_request:
author: dixpac
---
title: Make it possible to pass coverage value to commit status API
merge_request: 9214
author: wendy0402
---
title: Remove inactive default email services
merge_request: 8987
author:
---
title: replace npm with yarn and add yarn.lock
merge_request: 9055
author:
---
title: Replace static fixture for behaviors/requires_input_spec.js
merge_request: 9162
author: winniehell
---
title: Remove deprecated GitlabCiService
merge_request:
author:
...@@ -15,6 +15,13 @@ module.exports = function(config) { ...@@ -15,6 +15,13 @@ module.exports = function(config) {
preprocessors: { preprocessors: {
'spec/javascripts/**/*.js?(.es6)': ['webpack', 'sourcemap'], 'spec/javascripts/**/*.js?(.es6)': ['webpack', 'sourcemap'],
}, },
reporters: ['progress', 'coverage-istanbul'],
coverageIstanbulReporter: {
reports: ['html', 'text-summary'],
dir: 'coverage-javascript/',
subdir: '.',
fixWebpackSourcePaths: true
},
webpack: webpackConfig, webpack: webpackConfig,
webpackMiddleware: { stats: 'errors-only' }, webpackMiddleware: { stats: 'errors-only' },
}); });
......
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.
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