Commit 600d9d07 authored by Rémy Coutable's avatar Rémy Coutable

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

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents 68be6738 6038355f
...@@ -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"
} }
} }
...@@ -109,7 +109,10 @@ setup-test-env: ...@@ -109,7 +109,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:
...@@ -228,13 +231,12 @@ karma: ...@@ -228,13 +231,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
...@@ -307,11 +309,9 @@ lint:javascript: ...@@ -307,11 +309,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
...@@ -319,12 +319,10 @@ lint:javascript:report: ...@@ -319,12 +319,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
...@@ -377,7 +375,7 @@ pages: ...@@ -377,7 +375,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:
......
...@@ -21,7 +21,11 @@ Lint/AmbiguousRegexpLiteral: ...@@ -21,7 +21,11 @@ Lint/AmbiguousRegexpLiteral:
Lint/AssignmentInCondition: Lint/AssignmentInCondition:
Enabled: false Enabled: false
<<<<<<< HEAD
# Offense count: 22 # Offense count: 22
=======
# Offense count: 20
>>>>>>> ce/master
Lint/HandleExceptions: Lint/HandleExceptions:
Enabled: false Enabled: false
...@@ -76,12 +80,20 @@ Performance/RedundantMatch: ...@@ -76,12 +80,20 @@ Performance/RedundantMatch:
Performance/RedundantMerge: Performance/RedundantMerge:
Enabled: false Enabled: false
<<<<<<< HEAD
# Offense count: 26 # Offense count: 26
=======
# Offense count: 15
>>>>>>> ce/master
# Configuration parameters: CustomIncludeMethods. # Configuration parameters: CustomIncludeMethods.
RSpec/EmptyExampleGroup: RSpec/EmptyExampleGroup:
Enabled: false Enabled: false
<<<<<<< HEAD
# Offense count: 83 # Offense count: 83
=======
# Offense count: 58
>>>>>>> ce/master
# Configuration parameters: EnforcedStyle, SupportedStyles. # Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: implicit, each, example # SupportedStyles: implicit, each, example
RSpec/HookArgument: RSpec/HookArgument:
......
...@@ -188,6 +188,7 @@ entry. ...@@ -188,6 +188,7 @@ entry.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. - Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support. - Patch XSS vulnerability in RDOC support.
<<<<<<< HEAD
## 8.15.5 (2017-01-20) ## 8.15.5 (2017-01-20)
- Ensure export files are removed after a namespace is deleted. - Ensure export files are removed after a namespace is deleted.
...@@ -196,6 +197,8 @@ entry. ...@@ -196,6 +197,8 @@ entry.
- Prevent users from deleting system deploy keys via the project deploy key API. - Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2. - Upgrade omniauth gem to 1.3.2.
=======
>>>>>>> ce/master
## 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
......
...@@ -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'
......
...@@ -507,6 +507,8 @@ GEM ...@@ -507,6 +507,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)
...@@ -961,6 +963,7 @@ DEPENDENCIES ...@@ -961,6 +963,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)
......
...@@ -11,7 +11,10 @@ ...@@ -11,7 +11,10 @@
licensePath: "/api/:version/templates/licenses/:key", licensePath: "/api/:version/templates/licenses/:key",
gitignorePath: "/api/:version/templates/gitignores/:key", gitignorePath: "/api/:version/templates/gitignores/:key",
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
<<<<<<< HEAD
ldapGroupsPath: "/api/:version/ldap/:provider/groups.json", ldapGroupsPath: "/api/:version/ldap/:provider/groups.json",
=======
>>>>>>> ce/master
dockerfilePath: "/api/:version/templates/dockerfiles/:key", dockerfilePath: "/api/:version/templates/dockerfiles/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
group: function(group_id, callback) { group: function(group_id, callback) {
......
...@@ -58,8 +58,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/)); ...@@ -58,8 +58,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);
}); });
...@@ -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);
} }
......
...@@ -13,6 +13,12 @@ ...@@ -13,6 +13,12 @@
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<span v-if="items.length === 50" class="events-info pull-right">
<i class="fa fa-warning has-tooltip"
title="Limited to showing 50 events at most"
data-placement="top"></i>
Showing 50 events
</span>
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item"> <li v-for="commit in items" class="stage-event-item">
......
...@@ -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);
......
...@@ -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,
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
bindEvents() { bindEvents() {
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm); $('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
$('#user_notification_email').on('change', this.submitForm); $('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm);
$('.update-username').on('ajax:before', this.beforeUpdateUsername); $('.update-username').on('ajax:before', this.beforeUpdateUsername);
$('.update-username').on('ajax:complete', this.afterUpdateUsername); $('.update-username').on('ajax:complete', this.afterUpdateUsername);
$('.update-notifications').on('ajax:success', this.onUpdateNotifs); $('.update-notifications').on('ajax:success', this.onUpdateNotifs);
......
...@@ -128,8 +128,7 @@ ...@@ -128,8 +128,7 @@
.note-action-button .link-highlight, .note-action-button .link-highlight,
.toolbar-btn, .toolbar-btn,
.dropdown-toggle-caret, .dropdown-toggle-caret {
.fa:not(.fa-bell) {
@include transition(color); @include transition(color);
} }
......
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
.avatar { .avatar {
@extend .avatar-circle; @extend .avatar-circle;
@include transition-property(none);
width: 40px; width: 40px;
height: 40px; height: 40px;
padding: 0; padding: 0;
......
...@@ -284,7 +284,11 @@ ...@@ -284,7 +284,11 @@
.events-description { .events-description {
line-height: 65px; line-height: 65px;
padding-left: $gl-padding; padding: 0 $gl-padding;
}
.events-info {
color: $gl-text-color-secondary;
} }
} }
......
...@@ -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;
......
...@@ -85,14 +85,18 @@ ...@@ -85,14 +85,18 @@
-webkit-align-items: center; -webkit-align-items: center;
align-items: center; align-items: center;
i,
svg {
margin-right: 8px;
}
svg { svg {
margin-right: 4px;
position: relative; position: relative;
top: 1px; top: 1px;
overflow: visible; overflow: visible;
} }
&> span { & > span {
padding-right: 4px; padding-right: 4px;
} }
......
...@@ -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;
......
...@@ -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;
......
.new-wiki-page {
.new-wiki-page-slug-tip {
display: inline-block;
max-width: 100%;
margin-top: 5px;
}
}
.title .edit-wiki-header { .title .edit-wiki-header {
width: 780px; width: 780px;
margin-left: auto; margin-left: auto;
...@@ -9,12 +17,18 @@ ...@@ -9,12 +17,18 @@
@extend .top-area; @extend .top-area;
position: relative; position: relative;
.wiki-breadcrumb {
border-bottom: 1px solid $white-normal;
padding: 11px 0;
}
.wiki-page-title { .wiki-page-title {
margin: 0; margin: 0;
font-size: 22px; font-size: 22px;
} }
.wiki-last-edit-by { .wiki-last-edit-by {
display: block;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
strong { strong {
...@@ -121,6 +135,10 @@ ...@@ -121,6 +135,10 @@
margin: 5px 0 10px; margin: 5px 0 10px;
} }
ul.wiki-pages ul {
padding-left: 15px;
}
.wiki-sidebar-header { .wiki-sidebar-header {
padding: 0 $gl-padding $gl-padding; padding: 0 $gl-padding $gl-padding;
...@@ -129,3 +147,15 @@ ...@@ -129,3 +147,15 @@
} }
} }
} }
ul.wiki-pages-list.content-list {
& ul {
list-style: none;
margin-left: 0;
padding-left: 15px;
}
& ul li {
padding: 5px 0;
}
}
...@@ -17,6 +17,6 @@ class Profiles::NotificationsController < Profiles::ApplicationController ...@@ -17,6 +17,6 @@ class Profiles::NotificationsController < Profiles::ApplicationController
end end
def user_params def user_params
params.require(:user).permit(:notification_email) params.require(:user).permit(:notification_email, :notified_of_own_activity)
end end
end end
...@@ -8,6 +8,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -8,6 +8,7 @@ class Projects::WikisController < Projects::ApplicationController
def pages def pages
@wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]) @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page])
@wiki_entries = WikiPage.group_by_directory(@wiki_pages)
end end
def show def show
...@@ -87,6 +88,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -87,6 +88,7 @@ class Projects::WikisController < Projects::ApplicationController
def destroy def destroy
@page = @project_wiki.find_page(params[:id]) @page = @project_wiki.find_page(params[:id])
<<<<<<< HEAD
if @page if @page
@page.delete @page.delete
...@@ -94,6 +96,9 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -94,6 +96,9 @@ class Projects::WikisController < Projects::ApplicationController
# Triggers repository update on secondary nodes when Geo is enabled # Triggers repository update on secondary nodes when Geo is enabled
Gitlab::Geo.notify_wiki_update(@project) if Gitlab::Geo.primary? Gitlab::Geo.notify_wiki_update(@project) if Gitlab::Geo.primary?
end end
=======
WikiPages::DestroyService.new(@project, current_user).execute(@page)
>>>>>>> ce/master
redirect_to( redirect_to(
namespace_project_wiki_path(@project.namespace, @project, :home), namespace_project_wiki_path(@project.namespace, @project, :home),
...@@ -126,7 +131,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -126,7 +131,7 @@ class Projects::WikisController < Projects::ApplicationController
# Call #wiki to make sure the Wiki Repo is initialized # Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki @project_wiki.wiki
@sidebar_wiki_pages = @project_wiki.pages.first(15) @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages.first(15))
rescue ProjectWiki::CouldNotCreateWikiError rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project) redirect_to project_path(@project)
......
module BuildsHelper module BuildsHelper
def sidebar_build_class(build, current_build) def sidebar_build_class(build, current_build)
build_class = '' build_class = ''
build_class += ' active' if build == current_build build_class += ' active' if build.id === current_build.id
build_class += ' retried' if build.retried? build_class += ' retried' if build.retried?
build_class build_class
end end
......
...@@ -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
[ [
......
...@@ -15,6 +15,7 @@ module TodosHelper ...@@ -15,6 +15,7 @@ module TodosHelper
when Todo::MARKED then 'added a todo for' when Todo::MARKED then 'added a todo for'
when Todo::APPROVAL_REQUIRED then 'set you as an approver for' when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
when Todo::UNMERGEABLE then 'Could not merge' when Todo::UNMERGEABLE then 'Could not merge'
when Todo::DIRECTLY_ADDRESSED then 'directly addressed you on'
end end
end end
...@@ -88,7 +89,8 @@ module TodosHelper ...@@ -88,7 +89,8 @@ module TodosHelper
{ id: Todo::ASSIGNED, text: 'Assigned' }, { id: Todo::ASSIGNED, text: 'Assigned' },
{ id: Todo::MENTIONED, text: 'Mentioned' }, { id: Todo::MENTIONED, text: 'Mentioned' },
{ id: Todo::MARKED, text: 'Added' }, { id: Todo::MARKED, text: 'Added' },
{ id: Todo::BUILD_FAILED, text: 'Pipelines' } { id: Todo::BUILD_FAILED, text: 'Pipelines' },
{ id: Todo::DIRECTLY_ADDRESSED, text: 'Directly addressed' }
] ]
end end
......
module WikiHelper
# Produces a pure text breadcrumb for a given page.
#
# page_slug - The slug of a WikiPage object.
#
# Returns a String composed of the capitalized name of each directory and the
# capitalized name of the page itself.
def breadcrumb(page_slug)
page_slug.split('/').
map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize }.
join(' / ')
end
end
...@@ -44,8 +44,15 @@ module Mentionable ...@@ -44,8 +44,15 @@ module Mentionable
end end
def all_references(current_user = nil, extractor: nil) def all_references(current_user = nil, extractor: nil)
extractor ||= Gitlab::ReferenceExtractor. # Use custom extractor if it's passed in the function parameters.
new(project, current_user) if extractor
@extractor = extractor
else
@extractor ||= Gitlab::ReferenceExtractor.
new(project, current_user)
@extractor.reset_memoized_values
end
self.class.mentionable_attrs.each do |attr, options| self.class.mentionable_attrs.each do |attr, options|
text = __send__(attr) text = __send__(attr)
...@@ -55,16 +62,20 @@ module Mentionable ...@@ -55,16 +62,20 @@ module Mentionable
skip_project_check: skip_project_check? skip_project_check: skip_project_check?
) )
extractor.analyze(text, options) @extractor.analyze(text, options)
end end
extractor @extractor
end end
def mentioned_users(current_user = nil) def mentioned_users(current_user = nil)
all_references(current_user).users all_references(current_user).users
end end
def directly_addressed_users(current_user = nil)
all_references(current_user).directly_addressed_users
end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def referenced_mentionables(current_user = self.author) def referenced_mentionables(current_user = self.author)
refs = all_references(current_user) refs = all_references(current_user)
......
class DirectlyAddressedUser
class << self
def reference_pattern
User.reference_pattern
end
end
end
...@@ -50,9 +50,10 @@ class Event < ActiveRecord::Base ...@@ -50,9 +50,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)
......
...@@ -97,7 +97,7 @@ class Group < Namespace ...@@ -97,7 +97,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
......
...@@ -584,7 +584,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -584,7 +584,7 @@ class MergeRequest < ActiveRecord::Base
# Return the set of issues that will be closed if this merge request is accepted. # Return the set of issues that will be closed if this merge request is accepted.
def closes_issues(current_user = self.author) def closes_issues(current_user = self.author)
if target_branch == project.default_branch if target_branch == project.default_branch
messages = [description] messages = [title, description]
messages.concat(commits.map(&:safe_message)) if merge_request_diff messages.concat(commits.map(&:safe_message)) if merge_request_diff
Gitlab::ClosingIssueExtractor.new(project, current_user). Gitlab::ClosingIssueExtractor.new(project, current_user).
...@@ -598,7 +598,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -598,7 +598,7 @@ class MergeRequest < ActiveRecord::Base
return [] unless target_branch == project.default_branch return [] unless target_branch == project.default_branch
ext = Gitlab::ReferenceExtractor.new(project, current_user) ext = Gitlab::ReferenceExtractor.new(project, current_user)
ext.analyze(description) ext.analyze("#{title}\n#{description}")
ext.issues - closes_issues(current_user) ext.issues - closes_issues(current_user)
end end
...@@ -621,7 +621,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -621,7 +621,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
...@@ -629,7 +629,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -629,7 +629,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
......
...@@ -493,7 +493,7 @@ class Project < ActiveRecord::Base ...@@ -493,7 +493,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
...@@ -1057,8 +1057,8 @@ class Project < ActiveRecord::Base ...@@ -1057,8 +1057,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.
...@@ -1298,19 +1298,25 @@ class Project < ActiveRecord::Base ...@@ -1298,19 +1298,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
...@@ -1327,8 +1333,8 @@ class Project < ActiveRecord::Base ...@@ -1327,8 +1333,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
...@@ -1432,7 +1438,7 @@ class Project < ActiveRecord::Base ...@@ -1432,7 +1438,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
...@@ -1440,7 +1446,7 @@ class Project < ActiveRecord::Base ...@@ -1440,7 +1446,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
......
...@@ -1319,6 +1319,14 @@ class Repository ...@@ -1319,6 +1319,14 @@ class Repository
action[:content] action[:content]
end end
detect = CharlockHolmes::EncodingDetector.new.detect(content) if content
unless detect && detect[:type] == :binary
# When writing to the repo directly as we are doing here,
# the `core.autocrlf` config isn't taken into account.
content.gsub!("\r\n", "\n") if self.autocrlf
end
oid = rugged.write(content, :blob) oid = rugged.write(content, :blob)
index.add(path: path, oid: oid, mode: mode) index.add(path: path, oid: oid, mode: mode)
......
class Todo < ActiveRecord::Base class Todo < ActiveRecord::Base
include Sortable include Sortable
ASSIGNED = 1 ASSIGNED = 1
MENTIONED = 2 MENTIONED = 2
BUILD_FAILED = 3 BUILD_FAILED = 3
MARKED = 4 MARKED = 4
APPROVAL_REQUIRED = 5 # This is an EE-only feature APPROVAL_REQUIRED = 5 # This is an EE-only feature
UNMERGEABLE = 6 UNMERGEABLE = 6
DIRECTLY_ADDRESSED = 7
ACTION_NAMES = { ACTION_NAMES = {
ASSIGNED => :assigned, ASSIGNED => :assigned,
...@@ -14,7 +15,8 @@ class Todo < ActiveRecord::Base ...@@ -14,7 +15,8 @@ class Todo < ActiveRecord::Base
BUILD_FAILED => :build_failed, BUILD_FAILED => :build_failed,
MARKED => :marked, MARKED => :marked,
APPROVAL_REQUIRED => :approval_required, APPROVAL_REQUIRED => :approval_required,
UNMERGEABLE => :unmergeable UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed
} }
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
......
...@@ -53,7 +53,12 @@ class User < ActiveRecord::Base ...@@ -53,7 +53,12 @@ class User < ActiveRecord::Base
has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id
# Profile # Profile
has_many :keys, dependent: :destroy has_many :keys, -> do
type = Key.arel_table[:type]
where(type.not_eq('DeployKey').or(type.eq(nil)))
end, dependent: :destroy
has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy
has_many :emails, dependent: :destroy has_many :emails, dependent: :destroy
has_many :personal_access_tokens, dependent: :destroy has_many :personal_access_tokens, dependent: :destroy
has_many :identities, dependent: :destroy, autosave: true has_many :identities, dependent: :destroy, autosave: true
...@@ -355,7 +360,7 @@ class User < ActiveRecord::Base ...@@ -355,7 +360,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
......
class WikiDirectory
include ActiveModel::Validations
attr_accessor :slug, :pages
validates :slug, presence: true
def initialize(slug, pages = [])
@slug = slug
@pages = pages
end
# Relative path to the partial to be used when rendering collections
# of this object.
def to_partial_path
'projects/wikis/wiki_directory'
end
end
...@@ -12,6 +12,32 @@ class WikiPage ...@@ -12,6 +12,32 @@ class WikiPage
ActiveModel::Name.new(self, nil, 'wiki') ActiveModel::Name.new(self, nil, 'wiki')
end end
# Sorts and groups pages by directory.
#
# pages - an array of WikiPage objects.
#
# Returns an array of WikiPage and WikiDirectory objects. The entries are
# sorted by alphabetical order (directories and pages inside each directory).
# Pages at the root level come before everything.
def self.group_by_directory(pages)
return [] if pages.blank?
pages.sort_by { |page| [page.directory, page.slug] }.
group_by(&:directory).
map do |dir, pages|
if dir.present?
WikiDirectory.new(dir, pages)
else
pages
end
end.
flatten
end
def self.unhyphenize(name)
name.gsub(/-+/, ' ')
end
def to_key def to_key
[:slug] [:slug]
end end
...@@ -56,7 +82,7 @@ class WikiPage ...@@ -56,7 +82,7 @@ class WikiPage
# The formatted title of this page. # The formatted title of this page.
def title def title
if @attributes[:title] if @attributes[:title]
@attributes[:title].gsub(/-+/, ' ') self.class.unhyphenize(@attributes[:title])
else else
"" ""
end end
...@@ -72,6 +98,11 @@ class WikiPage ...@@ -72,6 +98,11 @@ class WikiPage
@attributes[:content] ||= @page&.text_data @attributes[:content] ||= @page&.text_data
end end
# The hierarchy of the directory this page is contained in.
def directory
wiki.page_title_and_dir(slug).last
end
# The processed/formatted content of this page. # The processed/formatted content of this page.
def formatted_content def formatted_content
@attributes[:formatted_content] ||= @page&.formatted_data @attributes[:formatted_content] ||= @page&.formatted_data
...@@ -170,6 +201,16 @@ class WikiPage ...@@ -170,6 +201,16 @@ class WikiPage
end end
end end
# Relative path to the partial to be used when rendering collections
# of this object.
def to_partial_path
'projects/wikis/wiki_page'
end
def id
page.version.to_s
end
private private
def set_attributes def set_attributes
......
...@@ -161,7 +161,11 @@ module MergeRequests ...@@ -161,7 +161,11 @@ module MergeRequests
return unless @commits.present? return unless @commits.present?
merge_requests_for_source_branch.each do |merge_request| merge_requests_for_source_branch.each do |merge_request|
wip_commit = @commits.detect(&:work_in_progress?) commit_shas = merge_request.commits_sha
wip_commit = @commits.detect do |commit|
commit.work_in_progress? && commit_shas.include?(commit.sha)
end
if wip_commit && !merge_request.work_in_progress? if wip_commit && !merge_request.work_in_progress?
merge_request.update(title: merge_request.wip_title) merge_request.update(title: merge_request.wip_title)
......
...@@ -234,7 +234,7 @@ class NotificationService ...@@ -234,7 +234,7 @@ class NotificationService
recipients = reject_unsubscribed_users(recipients, note.noteable) recipients = reject_unsubscribed_users(recipients, note.noteable)
recipients = reject_users_without_access(recipients, note.noteable) recipients = reject_users_without_access(recipients, note.noteable)
recipients.delete(note.author) recipients.delete(note.author) unless note.author.notified_of_own_activity?
recipients = recipients.uniq recipients = recipients.uniq
notify_method = "note_#{note.to_ability_name}_email".to_sym notify_method = "note_#{note.to_ability_name}_email".to_sym
...@@ -344,8 +344,9 @@ class NotificationService ...@@ -344,8 +344,9 @@ class NotificationService
recipients ||= build_recipients( recipients ||= build_recipients(
pipeline, pipeline,
pipeline.project, pipeline.project,
nil, # The acting user, who won't be added to recipients pipeline.user,
action: pipeline.status).map(&:notification_email) action: pipeline.status,
skip_current_user: false).map(&:notification_email)
if recipients.any? if recipients.any?
mailer.public_send(email_template, pipeline, recipients).deliver_later mailer.public_send(email_template, pipeline, recipients).deliver_later
...@@ -668,7 +669,7 @@ class NotificationService ...@@ -668,7 +669,7 @@ class NotificationService
recipients = reject_unsubscribed_users(recipients, target) recipients = reject_unsubscribed_users(recipients, target)
recipients = reject_users_without_access(recipients, target) recipients = reject_users_without_access(recipients, target)
recipients.delete(current_user) if skip_current_user recipients.delete(current_user) if skip_current_user && !current_user.notified_of_own_activity?
recipients.uniq recipients.uniq
end end
...@@ -677,7 +678,7 @@ class NotificationService ...@@ -677,7 +678,7 @@ class NotificationService
recipients = add_labels_subscribers([], project, target, labels: labels) recipients = add_labels_subscribers([], project, target, labels: labels)
recipients = reject_unsubscribed_users(recipients, target) recipients = reject_unsubscribed_users(recipients, target)
recipients = reject_users_without_access(recipients, target) recipients = reject_users_without_access(recipients, target)
recipients.delete(current_user) recipients.delete(current_user) unless current_user.notified_of_own_activity?
recipients.uniq recipients.uniq
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
......
...@@ -256,6 +256,12 @@ class TodoService ...@@ -256,6 +256,12 @@ class TodoService
end end
def create_mention_todos(project, target, author, note = nil) def create_mention_todos(project, target, author, note = nil)
# Create Todos for directly addressed users
directly_addressed_users = filter_directly_addressed_users(project, note || target, author)
attributes = attributes_for_todo(project, target, author, Todo::DIRECTLY_ADDRESSED, note)
create_todos(directly_addressed_users, attributes)
# Create Todos for mentioned users
mentioned_users = filter_mentioned_users(project, note || target, author) mentioned_users = filter_mentioned_users(project, note || target, author)
attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note) attributes = attributes_for_todo(project, target, author, Todo::MENTIONED, note)
create_todos(mentioned_users, attributes) create_todos(mentioned_users, attributes)
...@@ -300,10 +306,18 @@ class TodoService ...@@ -300,10 +306,18 @@ class TodoService
) )
end end
def filter_todo_users(users, project, target)
reject_users_without_access(users, project, target).uniq
end
def filter_mentioned_users(project, target, author) def filter_mentioned_users(project, target, author)
mentioned_users = target.mentioned_users(author) mentioned_users = target.mentioned_users(author)
mentioned_users = reject_users_without_access(mentioned_users, project, target) filter_todo_users(mentioned_users, project, target)
mentioned_users.uniq end
def filter_directly_addressed_users(project, target, author)
directly_addressed_users = target.directly_addressed_users(author)
filter_todo_users(directly_addressed_users, project, target)
end end
def reject_users_without_access(users, project, target) def reject_users_without_access(users, project, target)
......
module WikiPages
class DestroyService < WikiPages::BaseService
def execute(page)
if page&.delete
execute_hooks(page, 'delete')
end
page
end
end
end
...@@ -182,6 +182,6 @@ ...@@ -182,6 +182,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')
......
%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' />")
......
...@@ -34,6 +34,11 @@ ...@@ -34,6 +34,11 @@
.clearfix .clearfix
= form_for @user, url: profile_notifications_path, method: :put do |f|
%label{ for: 'user_notified_of_own_activity' }
= f.check_box :notified_of_own_activity
%span Receive notifications about your own activity
%hr %hr
%h5 %h5
Groups (#{@group_notifications.count}) Groups (#{@group_notifications.count})
......
...@@ -10,13 +10,13 @@ ...@@ -10,13 +10,13 @@
- if diff_file.renamed_file - if diff_file.renamed_file
- old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path) - old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
%strong.file-title-name.has-tooltip{ data: { title: old_path } } %strong.file-title-name.has-tooltip{ data: { title: old_path, container: 'body' } }
= old_path = old_path
&rarr; &rarr;
%strong.file-title-name.has-tooltip{ data: { title: new_path } } %strong.file-title-name.has-tooltip{ data: { title: new_path, container: 'body' } }
= new_path = new_path
- else - else
%strong.file-title-name.has-tooltip{ data: { title: diff_file.new_path } } %strong.file-title-name.has-tooltip{ data: { title: diff_file.new_path, container: 'body' } }
= diff_file.new_path = diff_file.new_path
- if diff_file.deleted_file - if diff_file.deleted_file
deleted deleted
......
...@@ -243,7 +243,7 @@ ...@@ -243,7 +243,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?
......
...@@ -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
......
...@@ -13,5 +13,9 @@ ...@@ -13,5 +13,9 @@
= label_tag :new_wiki_path do = label_tag :new_wiki_path do
%span Page slug %span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
%span.new-wiki-page-slug-tip
= icon('lightbulb-o')
Tip: You can specify the full path for the new file.
We will automatically create any missing directories.
.form-actions .form-actions
= button_tag 'Create Page', class: 'build-new-wiki btn btn-create' = button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
%li
= link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
...@@ -12,10 +12,8 @@ ...@@ -12,10 +12,8 @@
.blocks-container .blocks-container
.block.block-first .block.block-first
%ul.wiki-pages %ul.wiki-pages
- @sidebar_wiki_pages.each do |wiki_page| = render @sidebar_wiki_entries, context: 'sidebar'
%li{ class: params[:id] == wiki_page.slug ? 'active' : '' }
= link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do
= wiki_page.title.capitalize
.block .block
= link_to namespace_project_wikis_pages_path(@project.namespace, @project), class: 'btn btn-block' do = link_to namespace_project_wikis_pages_path(@project.namespace, @project), class: 'btn btn-block' do
More Pages More Pages
......
%li{ class: params[:id] == wiki_page.slug ? 'active' : '' }
= link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do
= wiki_page.title.capitalize
%li
= wiki_directory.slug
%ul
= render wiki_directory.pages, context: context
= render "#{context}_wiki_page", wiki_page: wiki_page
...@@ -13,11 +13,7 @@ ...@@ -13,11 +13,7 @@
= icon('cloud-download') = icon('cloud-download')
Clone repository Clone repository
%ul.content-list %ul.wiki-pages-list.content-list
- @wiki_pages.each do |wiki_page| = render @wiki_entries, context: 'pages'
%li
= link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
= paginate @wiki_pages, theme: 'gitlab' = paginate @wiki_pages, theme: 'gitlab'
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
= icon('angle-double-left') = icon('angle-double-left')
.wiki-breadcrumb
%span= breadcrumb(@page.slug)
.nav-text .nav-text
%h2.wiki-page-title= @page.title.capitalize %h2.wiki-page-title= @page.title.capitalize
%span.wiki-last-edit-by %span.wiki-last-edit-by
Last edited by Last edited by
%strong %strong
......
...@@ -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: Execute web hooks for WikiPage delete operation
merge_request: 8198
author:
---
title: Add discussion events to contributions calendar
merge_request: 8821
author:
---
title: V3 deprecated templates endpoints removal
merge_request: 8853
author:
---
title: Show directory hierarchy when listing wiki pages
merge_request: 8133
author: Alex Braha Stoll
---
title: Show Issues mentioned / being closed from a Merge Requests title below the
'Accept Merge Request' button
merge_request: 9194
author: Jan Christophersen
---
title: Added a feature to create a 'directly addressed' Todo when mentioned in the beginning of a line.
merge_request: 7926
author: Ershad Kunnakkadan
---
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: Add the oauth2_generic OmniAuth strategy
merge_request: 9048
author: Joe Marty
\ No newline at end of file
---
title: Do not display deploy keys in user's own ssh keys list
merge_request: 9024
author:
---
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: Set maximum width for mini pipeline graph text so it is not truncated to early
merge_request: 9188
author:
---
title: Fix Merge request pipelines displays JSON
merge_request:
author:
---
title: Fix current build arrow indicator
merge_request:
author:
---
title: Add space between text and loading icon in Megre Request Widget
merge_request: 9119
author:
---
title: Display loading indicator when filtering ref switcher dropdown
merge_request:
author:
---
title: Adds container to tooltip in order to make it work with overflow:hidden in
parent element
merge_request:
author:
---
title: Fix z index issues with sidebar
merge_request:
author:
---
title: Reintroduce coverage report for JavaScript
merge_request: 9133
author: winniehell
---
title: Replace static fixture for header_spec.js
merge_request: 9174
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: Add limit to the number of events showed in cycle analytics
merge_request:
author:
---
title: Fix timezone on issue boards due date
merge_request:
author:
---
title: Replace static fixture for merge_request_tabs_spec.js
merge_request: 9172
author: winniehell
---
title: Replace static fixture for new_branch_spec.js
merge_request: 9131
author: winniehell
---
title: Add option to receive email notifications about your own activity
merge_request: 8836
author: Richard Macklin
---
title: Make it possible to pass coverage value to commit status API
merge_request: 9214
author: wendy0402
---
title: Replace static fixture for behaviors/quick_submit_spec.js
merge_request: 9086
author: winniehell
---
title: Remove inactive default email services
merge_request: 8987
author:
---
title: replace npm with yarn and add yarn.lock
merge_request: 9055
author:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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