Commit dec16893 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge remote-tracking branch 'dev_gitlab_org/master' into git-http-blacklist

Conflicts:
	CHANGELOG
parents af56c1dd d02a22ba
*.log
*.swp
.DS_Store
.bundle
.chef
.directory
.envrc
.gitlab_shell_secret
.idea
.rbenv-version
.rbx/
db/*.sqlite3
db/*.sqlite3-journal
log/*.log*
tmp/
.sass-cache/
coverage/*
backups/*
*.swp
public/uploads/
.ruby-version
.ruby-gemset
.ruby-version
.rvmrc
.rbenv-version
.directory
nohup.out
Vagrantfile
.sass-cache/
.secret
.vagrant
config/gitlab.yml
Vagrantfile
backups/*
config/aws.yml
config/database.yml
config/gitlab.yml
config/initializers/omniauth.rb
config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb
config/unicorn.rb
config/resque.yml
config/aws.yml
config/unicorn.rb
coverage/*
db/*.sqlite3
db/*.sqlite3-journal
db/data.yml
.idea
.DS_Store
.chef
vendor/bundle/*
rails_best_practices_output.html
doc/code/*
.secret
*.log
public/uploads.*
public/assets/
.envrc
dump.rdb
log/*.log*
nohup.out
public/assets/
public/uploads.*
public/uploads/
rails_best_practices_output.html
tags
.gitlab_shell_secret
tmp/
vendor/bundle/*
v 7.7.0
-
-
- Add Jetbrains Teamcity CI service (Jason Lippert)
-
-
- Mention notification level
- Markdown preview in wiki (Yuriy Glukhov)
- Raise group avatar filesize limit to 200kb
- OAuth applications feature
- Show user SSH keys in admin area
- Developer can push to protected branches option
- Set project path instead of project name in create form
- Block Git HTTP access after 10 failed authentication attempts
-
-
- Updates to the messages returned by API (sponsored by O'Reilly Media)
- New UI layout with side navigation
-
-
-
- Add alert message in case of outdated browser (IE < 10)
-
- Added API support for sorting projects
- Update gitlab_git to version 7.0.0.rc13
v 7.6.0
- Fork repository to groups
- New rugged version
- Add CRON=1 backup setting for quiet backups
- Fix failing wiki restore
-
- Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable)
-
-
- Monokai highlighting style now more faithful to original design (Mark Riedesel)
- Create project with repository in synchrony
- Added ability to create empty repo or import existing one if project does not have repository
-
-
- Reactivate highlight.js language autodetection
- Mobile UI improvements
-
- Change maximum avatar file size from 100KB to 200KB
- Strict validation for snippet file names
- Enable Markdown preview for issues, merge requests, milestones, and notes (Vinnie Okada)
......@@ -25,9 +42,21 @@ v 7.6.0
- Update Sidekiq to version 2.17.8
- Add author filter to project issues and merge requests pages
- Atom feed for user activity
- Support multiple omniauth providers for the same user
- Rendering cross reference in issue title and tooltip for merge request
- Show username in comments
- Possibility to create Milestones or Labels when Issues are disabled
- Fix bug with showing gpg signature in tag
v 7.5.3
- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
v 7.5.2
- Don't log Sidekiq arguments by default
- Fix restore of wiki repositories from backups
v 7.5.1
- Add missing timestamps to 'members' table
v 7.5.0
- API: Add support for Hipchat (Kevin Houdebert)
......@@ -47,7 +76,7 @@ v 7.5.0
- Performance improvements
- Fix post-receive issue for projects with deleted forks
- New gitlab-shell version with custom hooks support
- Improve code
- Improve code
- GitLab CI 5.2+ support (does not support older versions)
- Fixed bug when you can not push commits starting with 000000 to protected branches
- Added a password strength indicator
......
......@@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps.
1. Community questions answered
1. Answers to questions radiated (in docs/wiki/etc.)
If you add a dependency in GitLab (such as an operating system package) please consider updating the following and note the applicability of each in your merge request:
1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
1. Test suite https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/configure_a_runner_to_run_the_gitlab_ce_test_suite.md
1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
## Merge request description format
1. What does this MR do?
......@@ -118,6 +128,7 @@ Please ensure you support the feature you contribute through all of these steps.
1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server)
1. Does not break any existing functionality
1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed)
1. Migrations should do only one thing (eg: either create a table, move data to a new table or remove an old table) to aid retrying on failure
1. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes
......
......@@ -28,10 +28,16 @@ gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
gem 'omniauth-shibboleth'
gem 'omniauth-kerberos'
gem 'doorkeeper', '2.0.1'
gem "rack-oauth2", "~> 1.0.5"
# Browser detection
gem "browser"
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '7.0.0.rc12'
gem "gitlab_git", '7.0.0.rc13'
# Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
......@@ -89,7 +95,7 @@ gem "github-markup"
gem 'redcarpet', '~> 3.1.2'
gem 'RedCloth'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.9'
gem 'org-ruby', '= 0.9.12'
gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4'
......
......@@ -37,6 +37,7 @@ GEM
rake (>= 0.8.7)
arel (5.0.1.20140414130214)
asciidoctor (0.1.4)
attr_required (1.0.0)
awesome_print (1.2.0)
axiom-types (0.0.5)
descendants_tracker (~> 0.0.1)
......@@ -49,6 +50,7 @@ GEM
debug_inspector (>= 0.0.1)
bootstrap-sass (3.0.3.0)
sass (~> 3.2)
browser (0.7.2)
builder (3.2.2)
capybara (2.2.1)
mime-types (>= 1.16)
......@@ -107,6 +109,8 @@ GEM
diff-lcs (1.2.5)
diffy (3.0.3)
docile (1.1.5)
doorkeeper (2.0.1)
railties (>= 3.1)
dotenv (0.9.0)
dropzonejs-rails (0.4.14)
rails (> 3.1)
......@@ -120,7 +124,7 @@ GEM
equalizer (0.0.8)
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.3)
eventmachine (1.0.4)
excon (0.32.1)
execjs (2.0.2)
expression_parser (0.9.0)
......@@ -158,7 +162,7 @@ GEM
dotenv (>= 0.7)
thor (>= 0.13.6)
formatador (0.2.4)
gemnasium-gitlab-service (0.2.2)
gemnasium-gitlab-service (0.2.3)
rugged (~> 0.19)
gherkin-ruby (0.3.1)
racc
......@@ -179,7 +183,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1)
gitlab_git (7.0.0.rc12)
gitlab_git (7.0.0.rc13)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
......@@ -250,6 +254,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpauth (0.2.1)
httpclient (2.5.3.3)
i18n (0.6.11)
ice_nine (0.10.0)
jasmine (2.0.2)
......@@ -275,7 +280,7 @@ GEM
kaminari (0.15.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.8.1)
kgio (2.9.2)
launchy (2.4.2)
addressable (~> 2.3)
letter_opener (1.1.2)
......@@ -322,6 +327,11 @@ GEM
omniauth-google-oauth2 (0.2.5)
omniauth (> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-kerberos (0.2.0)
omniauth-multipassword
timfel-krb5-auth (~> 0.8)
omniauth-multipassword (0.4.1)
omniauth (~> 1.0)
omniauth-oauth (1.0.1)
oauth
omniauth (~> 1.0)
......@@ -333,7 +343,7 @@ GEM
omniauth-twitter (1.0.1)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
org-ruby (0.9.9)
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
pg (0.15.1)
......@@ -363,6 +373,12 @@ GEM
rack (>= 1.1.3)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-oauth2 (1.0.8)
activesupport (>= 2.3)
attr_required (>= 0.0.5)
httpclient (>= 2.2.0.2)
multi_json (>= 1.3.6)
rack (>= 1.1)
rack-protection (1.5.1)
rack
rack-test (0.6.2)
......@@ -393,7 +409,7 @@ GEM
activesupport (= 4.1.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
raindrops (0.12.0)
raindrops (0.13.0)
rake (10.3.2)
raphael-rails (2.1.2)
rb-fsevent (0.9.3)
......@@ -531,6 +547,7 @@ GEM
thread_safe (0.3.4)
tilt (1.4.1)
timers (1.1.0)
timfel-krb5-auth (0.8)
tinder (1.9.3)
eventmachine (~> 1.0)
faraday (~> 0.8)
......@@ -598,6 +615,7 @@ DEPENDENCIES
better_errors
binding_of_caller
bootstrap-sass (~> 3.0)
browser
capybara (~> 2.2.1)
carrierwave
coffee-rails
......@@ -610,6 +628,7 @@ DEPENDENCIES
devise (= 3.2.4)
devise-async (= 0.9.0)
diffy (~> 3.0.3)
doorkeeper (= 2.0.1)
dropzonejs-rails
email_spec
enumerize
......@@ -624,7 +643,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1)
gitlab_git (= 7.0.0.rc12)
gitlab_git (= 7.0.0.rc13)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.0)
gollum-lib (~> 3.0.0)
......@@ -655,9 +674,10 @@ DEPENDENCIES
omniauth (~> 1.1.3)
omniauth-github
omniauth-google-oauth2
omniauth-kerberos
omniauth-shibboleth
omniauth-twitter
org-ruby (= 0.9.9)
org-ruby (= 0.9.12)
pg
poltergeist (~> 1.5.1)
pry
......@@ -665,6 +685,7 @@ DEPENDENCIES
rack-attack
rack-cors
rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0)
rails_autolink (~> 1.1)
rails_best_practices
......
......@@ -104,3 +104,10 @@ This merge request has been closed because a request for more information has no
### Accepting merge requests
Is there a request on [the feature request forum](http://feedback.gitlab.com/forums/176466-general) that is similar to this? If so, can you make a comment with a link to it? Please be aware that new functionality that is not marked [accepting merge/pull requests](http://feedback.gitlab.com/forums/176466-general/status/796455) on the forum might not make it into GitLab. You might be asked to make changes and even after implementing them your feature might still be declined. If you want to reduce the chance of this happening please have a discussion in the forum first.
### Only accepting merge requests with green tests
We can only accept a merge request if all the tests are green. I've just
restarted the build. When the tests are still not passing after this restart and
you're sure that is does not have anything to do with your code changes, please
rebase with master to see if that solves the issue.
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell
worker: bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default
......@@ -52,69 +52,30 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a
Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options.
Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm).
You can access new installation with the login `root` and password `5iveL!fe`, after login you are required to set a unique password.
## Third-party applications
There are a lot of applications and API wrappers for GitLab.
Find them [on our website](https://about.gitlab.com/applications/).
### New versions
## New versions
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
### Upgrading
## Upgrading
For updating the the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For manual installations there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update).
## Run in production mode
The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually:
sudo service gitlab start
or by directly calling the script:
sudo /etc/init.d/gitlab start
Please login with `root` / `5iveL!fe`
## Install a development environment
We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
If you do not use the development kit you might need to copy the example development unicorn configuration file
If you do not use the GitLab Development Development kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone.
One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file:
cp config/unicorn.rb.example.development config/unicorn.rb
## Run in development mode
Start it with [Foreman](https://github.com/ddollar/foreman)
bundle exec foreman start -p 3000
or start each component separately:
bundle exec rails s
bin/background_jobs start
And surf to [localhost:3000](http://localhost:3000/) and login with `root` / `5iveL!fe`.
## Run the tests
- Run all tests:
bundle exec rake test
- [RSpec](http://rspec.info/) unit and functional tests.
All RSpec tests: `bundle exec rake spec`
Single RSpec file: `bundle exec rspec spec/controllers/commit_controller_spec.rb`
- [Spinach](https://github.com/codegram/spinach) integration tests.
All Spinach tests: `bundle exec rake spinach`
Single Spinach test: `bundle exec spinach features/project/issues/milestones.feature`
Instructions on how to start Gitlab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development).
## Documentation
......
7.6.0.pre
7.7.0.pre
......@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) ->
sender.parent().toggleClass "inactive"
sender.parent().toggleClass "active"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
if event_filters
......
@Api =
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
......@@ -51,6 +53,33 @@
).done (users) ->
callback(users)
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (group) ->
callback(group)
# Return groups list. Filtered by query
# Only active groups retrieved
groups: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.groups_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (groups) ->
callback(groups)
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
......
......@@ -51,12 +51,6 @@ window.ajaxGet = (url) ->
window.showAndHide = (selector) ->
window.errorMessage = (message) ->
ehtml = $("<p>")
ehtml.addClass("error_message")
ehtml.html(message)
ehtml
window.split = (val) ->
return val.split( /,\s*/ )
......
class @GroupsSelect
constructor: ->
$('.ajax-groups-select').each (i, select) =>
skip_ldap = $(select).hasClass('skip_ldap')
$(select).select2
placeholder: "Search for a group"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.groups query.term, skip_ldap, (groups) ->
data = { results: groups }
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id isnt ""
Api.group(id, callback)
formatResult: (args...) =>
@formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-groups-dropdown"
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
formatResult: (group) ->
if group.avatar_url
avatar = group.avatar_url
else
avatar = gon.default_avatar_url
"<div class='group-result'>
<div class='group-name'>#{group.name}</div>
<div class='group-path'>#{group.path}</div>
</div>"
formatSelection: (group) ->
group.name
class @Issue
constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#issue_assignee_id", ->
$(".context .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
if $("a.btn-close").length
......
......@@ -26,9 +26,9 @@ class @MergeRequest
initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#merge_request_assignee_id", ->
$(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
initMergeWidget: ->
......@@ -89,6 +89,9 @@ class @MergeRequest
this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
when 'commits'
this.$('.merge-request-tabs .commits-tab').addClass 'active'
this.$('.commits').show()
else
this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
......
......@@ -375,7 +375,7 @@ class @Notes
###
addDiffNote: (e) =>
e.preventDefault()
link = e.target
link = e.currentTarget
form = $(".js-new-note-form")
row = $(link).closest("tr")
nextRow = row.next()
......
$ ->
$(":checkbox").change ->
name = $(this).attr("name")
if name == "developers_can_push"
id = $(this).val()
checked = $(this).is(":checked")
url = $(this).data("url")
$.ajax
type: "PUT"
url: url
dataType: "json"
data:
id: id
developers_can_push: checked
success: ->
new Flash("Branch updated.", "notice")
location.reload true
error: ->
new Flash("Failed to update branch!", "alert")
......@@ -46,7 +46,7 @@ class @ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width() - 70
@width = $('.container').width() - 345
@height = 200
@x = null
@y = null
......@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.container').width()/2 - 100
@width = $('.container').width()/2 - 225
@height = 200
@x = null
@y = null
......
......@@ -207,24 +207,16 @@ li.note {
}
}
.no-ssh-key-message {
padding: 10px 0;
background: #C67;
margin: 0;
color: #FFF;
margin-top: -1px;
.browser-alert {
padding: 10px;
text-align: center;
background: #C67;
color: #fff;
font-weight: bold;
a {
color: #fff;
text-decoration: underline;
}
.links-xs {
text-align: center;
font-size: 16px;
padding: 5px;
}
}
.warning_message {
......@@ -282,7 +274,7 @@ img.emoji {
}
.navless-container {
margin-top: 20px;
margin-top: 68px;
}
.description-block {
......@@ -300,11 +292,17 @@ table {
.dashboard-intro-icon {
float: left;
text-align: center;
font-size: 32px;
color: #AAA;
padding: 5px 0;
width: 50px;
min-height: 100px;
width: 60px;
}
.dashboard-intro-text {
display: inline-block;
margin-left: -60px;
padding-left: 60px;
width: 100%;
}
.broadcast-message {
......@@ -355,3 +353,9 @@ table {
.task-status {
margin-left: 10px;
}
#nprogress .spinner {
top: auto !important;
bottom: 20px !important;
left: 20px !important;
}
......@@ -31,7 +31,12 @@ fieldset legend {
margin-bottom: 18px;
background-color: whitesmoke;
border-top: 1px solid #e5e5e5;
padding-left: 17%;
}
@media (min-width: $screen-sm-min) {
.form-actions {
padding-left: 17%;
}
}
label {
......@@ -88,7 +93,8 @@ label {
@include box-shadow(none);
}
.issuable-description {
.issuable-description,
.wiki-content {
margin-top: 35px;
}
......
/**
* Issue box:
* Huge block (one per page) for storing title, descripion and other information.
* Issue box for showing Open/Closed state:
* Used for Issue#show page, MergeRequest#show page etc
*
* CLasses:
* .issue-box - Regular box
*/
.issue-box {
color: #555;
margin:20px 0;
background: $box_bg;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
display: inline-block;
padding: 7px 13px;
font-weight: normal;
margin-right: 5px;
&.issue-box-closed {
.state {
background-color: #F3CECE;
border-color: $border_danger;
}
.state-label {
background-color: $bg_danger;
color: #FFF;
}
background-color: $bg_danger;
color: #FFF;
}
&.issue-box-merged {
.state {
background-color: #B7CEE7;
border-color: $border_primary;
}
.state-label {
background-color: $bg_primary;
color: #FFF;
}
background-color: $bg_primary;
color: #FFF;
}
&.issue-box-open {
.state {
background-color: #D6F1D7;
border-color: $bg_success;
}
.state-label {
background-color: $bg_success;
color: #FFF;
}
background-color: $bg_success;
color: #FFF;
}
&.issue-box-expired {
.state {
background-color: #EEE9B3;
border-color: #faebcc;
}
.state-label {
background: #cea61b;
color: #FFF;
}
}
.control-group {
margin-bottom: 0;
}
.state {
background-color: #f9f9f9;
}
.title {
font-size: 28px;
font-weight: normal;
line-height: 1.5;
margin: 0;
color: #333;
padding: 10px 15px;
}
.context {
border: none;
border-top: 1px solid #eee;
padding: 10px 15px;
// Reset text align for children
.text-right > * { text-align: left; }
@media (max-width: $screen-xs-max) {
// Don't right align on mobile
.text-right { text-align: left; }
.row .col-md-6 {
padding-top: 5px;
}
}
}
.description {
padding: 0 15px 10px 15px;
code {
white-space: pre-wrap;
}
}
.title, .context, .description {
.clearfix {
margin: 0;
}
}
.state-label {
font-size: 14px;
float: left;
font-weight: bold;
padding: 10px 15px;
}
.cross-project-ref {
float: left;
padding: 10px 15px;
}
.creator {
float: right;
padding: 10px 15px;
a {
text-decoration: underline;
}
background: #cea61b;
color: #FFF;
}
}
......@@ -65,6 +65,7 @@
.edit_note,
.issuable-description,
.milestone-description,
.wiki-content,
.merge-request-form {
.nav-tabs {
margin-bottom: 0;
......
......@@ -116,6 +116,18 @@ select {
}
}
.group-result {
.group-image {
float: left;
}
.group-name {
font-weight: bold;
}
.group-path {
color: #999;
}
}
.user-result {
.user-image {
float: left;
......
table {
&.table {
tr {
td, th {
padding: 8px 10px;
line-height: 20px;
vertical-align: middle;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC !important;
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
}
}
}
......@@ -74,6 +74,42 @@
}
}
}
.system-note .timeline-entry-inner {
.timeline-icon {
background: none;
margin-left: 12px;
margin-top: 0;
@include box-shadow(none);
span {
margin: 0 2px;
font-size: 16px;
color: #eeeeee;
}
}
.timeline-content {
background: none;
margin-left: 45px;
padding: 0px 15px;
&:after { border: 0; }
.note-header {
span { font-size: 12px; }
.avatar {
margin-right: 5px;
}
}
.note-text {
font-size: 12px;
margin-left: 20px;
}
}
}
}
@media (max-width: $screen-xs-max) {
......
......@@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary;
color: #666;
}
.nav-compact > li > a {
padding: 6px 12px;
}
.nav-small > li > a {
padding: 3px 5px;
font-size: 12px;
......
......@@ -4,10 +4,6 @@ html {
&.touch .tooltip { display: none !important; }
}
body {
padding-bottom: 20px;
}
.container {
padding-top: 0;
z-index: 5;
......
......@@ -44,6 +44,6 @@ $added: #63c363;
$deleted: #f77;
/**
*
* NProgress customize
*/
$nprogress-color: #3498db;
$nprogress-color: #c0392b;
......@@ -23,20 +23,6 @@
}
}
.dashboard {
.dash-filter {
width: 205px;
float: left;
height: inherit;
}
}
@media (max-width: 1200px) {
.dashboard .dash-filter {
width: 140px;
}
}
.dash-sidebar-tabs {
margin-bottom: 2px;
border: none;
......
......@@ -140,43 +140,17 @@
}
}
/**
* Event filter
*
*/
.event_filter {
position: absolute;
width: 40px;
margin-left: -55px;
.filter_icon {
a {
text-align:center;
background: $bg_primary;
margin-bottom: 10px;
float: left;
padding: 9px 6px;
font-size: 18px;
width: 40px;
color: #FFF;
@include border-radius(3px);
}
&.inactive {
a {
color: #DDD;
background: #f9f9f9;
}
}
}
}
/*
* Last push widget
*/
.event-last-push {
overflow: auto;
.event-last-push-text {
@include str-truncated(75%);
@include str-truncated(100%);
float:left;
margin-right: -150px;
padding-right: 150px;
line-height: 24px;
}
}
......@@ -203,3 +177,7 @@
}
}
}
.event_filter li a {
padding: 5px 10px;
}
......@@ -4,9 +4,13 @@
*/
header {
&.navbar-gitlab {
z-index: 100;
margin-bottom: 0;
min-height: 40px;
border: none;
position: fixed;
top: 0;
width: 100%;
.navbar-inner {
filter: none;
......@@ -52,8 +56,6 @@ header {
border-width: 0;
font-size: 18px;
.app_logo { margin-left: -15px; }
.title {
@include str-truncated(70%);
}
......@@ -84,7 +86,10 @@ header {
}
}
z-index: 10;
.container {
width: 100% !important;
padding-left: 0px;
}
/**
*
......@@ -232,21 +237,6 @@ header {
color: #fff;
}
}
.app_logo {
.separator {
margin-left: 0;
margin-right: 0;
}
}
.separator {
float: left;
height: 46px;
width: 2px;
margin-left: 10px;
margin-right: 10px;
}
}
.search .search-input {
......
......@@ -162,3 +162,7 @@ form.edit-issue {
}
}
}
.issue-title {
margin-top: 0;
}
/* Login Page */
.login-page {
h1 {
font-size: 3em;
font-weight: 200;
.container {
max-width: 960px;
}
.login-box{
padding: 0 15px;
.navbar-gitlab .container {
max-width: none;
}
.login-heading h3 {
font-weight: 300;
line-height: 2;
}
.brand-holder {
font-size: 18px;
line-height: 1.5;
.login-footer {
margin-top: 10px;
p {
color: #888;
}
.btn {
padding: 12px !important;
@extend .btn-block;
h1:first-child {
font-weight: normal;
margin-bottom: 30px;
}
}
.brand-image {
img {
max-width: 100%;
margin-bottom: 20px;
margin-bottom: 30px;
}
&.default-brand-image {
margin: 0 80px;
a {
font-weight: bold;
}
}
.login-logo {
margin: 10px 0 30px 0;
display: block;
.login-box{
background: #fafafa;
border-radius: 10px;
box-shadow: 0 0px 2px #CCC;
padding: 15px;
.login-heading h3 {
font-weight: 300;
line-height: 1.5;
margin: 0;
display: none;
}
.login-footer {
margin-top: 10px;
}
a.forgot {
float: right;
padding-top: 6px
}
.nav .active a {
background: transparent;
}
}
.form-control {
background-color: #F5F5F5;
font-size: 16px;
padding: 14px 10px;
font-size: 14px;
padding: 10px 8px;
width: 100%;
height: auto;
......@@ -68,11 +86,6 @@
}
}
.login-box a.forgot {
float: right;
padding-top: 6px
}
.devise-errors {
h2 {
font-size: 14px;
......@@ -80,7 +93,19 @@
}
}
.brand-holder {
border-right: 1px solid #EEE;
.remember-me {
margin-top: -10px;
label {
font-weight: normal;
}
}
}
@media (max-width: $screen-xs-max) {
.login-page {
.col-sm-5.pull-right {
float: none !important;
}
}
}
......@@ -11,25 +11,40 @@
}
}
.accept-group {
label {
margin: 5px;
.accept-merge-holder {
margin-top: 5px;
.accept-action {
display: inline-block;
.accept_merge_request {
padding: 10px 20px;
}
}
.accept-control {
display: inline-block;
margin-left: 20px;
padding: 10px 0;
line-height: 20px;
font-weight: bold;
.checkbox {
margin: 0;
}
}
}
}
.merge-request .merge-request-tabs{
border-bottom: 2px solid $border_primary;
margin: 20px 0;
@media(min-width: $screen-sm-max) {
.merge-request .merge-request-tabs{
margin: 20px 0;
li {
a {
padding: 15px 40px;
font-size: 14px;
margin-bottom: -2px;
border-bottom: 2px solid $border_primary;
@include border-radius(0px);
li {
a {
padding: 15px 40px;
font-size: 14px;
}
}
}
}
......@@ -106,6 +121,7 @@
.mr-state-widget {
background: $box_bg;
margin-bottom: 20px;
color: #666;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget {
......@@ -150,7 +166,6 @@
padding: 10px 15px;
h4 {
font-size: 20px;
font-weight: normal;
}
......@@ -172,7 +187,3 @@
.merge-request-show-labels .label {
padding: 6px 10px;
}
.mr-commits .commit {
padding: 10px 15px;
}
.main-nav {
background: #f5f5f5;
margin: 20px 0;
margin-top: 0;
padding-top: 4px;
border-bottom: 1px solid #E9E9E9;
ul {
padding: 0;
margin: auto;
.count {
font-weight: normal;
display: inline-block;
height: 15px;
padding: 1px 6px;
height: auto;
font-size: 0.82em;
line-height: 14px;
text-align: center;
color: #777;
background: #eee;
@include border-radius(8px);
}
.label {
background: $hover;
text-shadow: none;
color: $style_color;
}
li {
list-style-type: none;
margin: 0;
display: table-cell;
width: 1%;
&.active {
a {
color: $link_color;
font-weight: bold;
border-bottom: 3px solid $link_color;
}
}
&:hover {
a {
color: $link_hover_color;
border-bottom: 3px solid $link_hover_color;
}
}
}
a {
display: block;
text-align: center;
font-weight: bold;
height: 42px;
line-height: 39px;
color: #777;
text-shadow: 0 1px 1px white;
text-decoration: none;
overflow: hidden;
margin-bottom: -1px;
}
}
@media (max-width: $screen-xs-max) {
font-size: 18px;
margin: 0;
max-height: none;
&, .container {
padding: 0;
border-top: 0;
}
ul {
height: auto;
li {
display: list-item;
width: auto;
padding: 5px 0;
&.active {
background-color: $link_hover_color;
a {
color: #fff;
font-weight: normal;
text-shadow: none;
border: none;
&:after { display: none; }
}
}
}
}
}
}
......@@ -62,6 +62,7 @@ ul.notes {
}
.note-body {
@include md-typography;
overflow: auto;
}
.note-header {
padding-bottom: 3px;
......@@ -155,19 +156,26 @@ ul.notes {
}
.add-diff-note {
background: image-url("diff_note_add.png") no-repeat left 0;
border: none;
height: 22px;
margin-left: -65px;
margin-top: -4px;
@include border-radius(40px);
background: #FFF;
padding: 4px;
font-size: 16px;
color: $link_color;
margin-left: -60px;
position: absolute;
width: 22px;
z-index: 10;
transition: all 0.2s ease;
// "hide" it by default
opacity: 0.0;
filter: alpha(opacity=0);
&:hover {
font-size: 24px;
background: $bg_primary;
color: #FFF;
@include show-add-diff-note;
}
}
......
......@@ -308,3 +308,10 @@ ul.nav.nav-projects-tabs {
display: none;
}
}
table.table.protected-branches-list tr.no-border {
th, td {
border: 0;
}
}
.page-with-sidebar {
background: #F5F5F5;
}
.sidebar-wrapper {
z-index: 99;
overflow-y: auto;
background: #F5F5F5;
}
.content-wrapper {
width: 100%;
padding: 15px;
background: #FFF;
margin-top: 48px;
}
.nav-sidebar {
margin: 0;
list-style: none;
&.navbar-collapse {
padding: 0px !important;
}
}
.nav-sidebar li a .count {
float: right;
background: #eee;
padding: 0px 8px;
@include border-radius(6px);
}
.nav-sidebar li {
&.active a {
color: #111;
background: #EEE;
font-weight: bold;
&.no-highlight {
background: none;
}
i {
color: #444;
}
}
}
.nav-sidebar li {
&.separate-item {
border-top: 1px solid #ddd;
padding-top: 10px;
margin-top: 10px;
}
a {
color: #555;
display: block;
text-decoration: none;
padding: 6px 15px;
font-size: 13px;
line-height: 20px;
text-shadow: 0 1px 2px #FFF;
padding-left: 20px;
&:hover {
text-decoration: none;
color: #333;
background: #DDD;
}
&:active, &:focus {
text-decoration: none;
}
i {
width: 20px;
color: #888;
margin-right: 23px;
}
}
}
.sidebar-subnav {
margin-left: 0px;
padding-left: 0px;
li {
list-style: none;
}
}
@mixin expanded-sidebar {
.page-with-sidebar {
padding-left: 250px;
}
.sidebar-wrapper {
width: 250px;
position: fixed;
left: 250px;
height: 100%;
margin-left: -250px;
border-right: 1px solid #EAEAEA;
.nav-sidebar {
margin-top: 20px;
position: fixed;
top: 45px;
width: 250px;
}
}
.content-wrapper {
padding: 20px;
}
}
@mixin folded-sidebar {
.page-with-sidebar {
padding-left: 50px;
}
.sidebar-wrapper {
width: 52px;
position: fixed;
top: 0;
left: 0;
height: 100%;
border-right: 1px solid #EAEAEA;
overflow-x: hidden;
.nav-sidebar {
margin-top: 20px;
position: absolute;
top: 45px;
width: 52px;
li a {
padding-left: 18px;
font-size: 14px;
padding: 10px 15px;
text-align: center;
& > span {
display: none;
}
}
}
}
}
@media (max-width: $screen-sm-max) {
@include folded-sidebar;
}
@media(min-width: $screen-sm-max) {
@include expanded-sidebar;
}
......@@ -17,19 +17,6 @@
@include border-radius(0);
tr {
td, th {
padding: 8px 10px;
line-height: 20px;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC !important;
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
&:hover {
td {
background: $hover;
......
......@@ -37,13 +37,3 @@
margin: 0 8px;
}
.votes-holder {
float: right;
width: 250px;
@media (max-width: $screen-xs-max) {
width: 100%;
margin-top: 5px;
margin-bottom: 10px;
}
}
......@@ -9,17 +9,15 @@
.navbar-inner {
background: #F1F1F1;
border-bottom: 1px solid #DDD;
.app_logo {
background-color: #DDD;
}
.nav > li > a {
color: $style_color;
}
.separator {
background: #F9F9F9;
border-left: 1px solid #DDD;
}
}
}
}
.main-nav {
background: #FFF;
}
}
......@@ -23,9 +23,8 @@
background-color: #436;
}
}
.separator {
background: #436;
border-left: 1px solid #659;
.app_logo {
background-color: #325;
}
.nav > li > a {
color: #98C;
......
......@@ -23,9 +23,8 @@
background-color: #272727;
}
}
.separator {
background: #272727;
border-left: 1px solid #474747;
.app_logo {
background-color: #222;
}
}
}
......
......@@ -23,9 +23,8 @@
background-color: #373D47;
}
}
.separator {
background: #373D47;
border-left: 1px solid #575D67;
.app_logo {
background-color: #24272D;
}
.nav > li > a {
color: #979DA7;
......
......@@ -23,9 +23,8 @@
background-color: #018865;
}
}
.separator {
background: #018865;
border-left: 1px solid #11A885;
.app_logo {
background-color: #017855;
}
.nav > li > a {
color: #ADC;
......
......@@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController
def create
@group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name
@group.name = @group.path.dup unless @group.name
if @group.save
@group.add_owner(current_user)
......
class Admin::KeysController < Admin::ApplicationController
before_filter :user, only: [:show, :destroy]
def show
@key = user.keys.find(params[:id])
respond_to do |format|
format.html
format.js { render nothing: true }
end
end
def destroy
key = user.keys.find(params[:id])
respond_to do |format|
if key.destroy
format.html { redirect_to [:admin, user], notice: 'User key was successfully removed.' }
else
format.html { redirect_to [:admin, user], alert: 'Failed to remove user key.' }
end
end
end
protected
def user
@user ||= User.find_by!(username: params[:user_id])
end
def key_params
params.require(:user_id, :id)
end
end
......@@ -11,6 +11,7 @@ class Admin::UsersController < Admin::ApplicationController
def show
@personal_projects = user.personal_projects
@joined_projects = user.projects.joined(@user)
@keys = user.keys.order('id DESC')
end
def new
......@@ -118,7 +119,7 @@ class Admin::UsersController < Admin::ApplicationController
:email, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
:projects_limit, :can_create_group, :admin
:projects_limit, :can_create_group, :admin, :key_id
)
end
end
......@@ -239,4 +239,63 @@ class ApplicationController < ActionController::Base
redirect_to profile_path, notice: 'Please complete your profile with email address' and return
end
end
def set_filters_params
params[:sort] ||= 'newest'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@filter_params = params.dup
if @project
@filter_params[:project_id] = @project.id
elsif @group
@filter_params[:group_id] = @group.id
else
# TODO: this filter ignore issues/mr created in public or
# internal repos where you are not a member. Enable this filter
# or improve current implementation to filter only issues you
# created or assigned or mentioned
#@filter_params[:authorized_only] = true
end
@filter_params
end
def set_filter_values(collection)
assignee_id = @filter_params[:assignee_id]
author_id = @filter_params[:author_id]
milestone_id = @filter_params[:milestone_id]
@sort = @filter_params[:sort].try(:humanize)
@assignees = User.where(id: collection.pluck(:assignee_id))
@authors = User.where(id: collection.pluck(:author_id))
@milestones = Milestone.where(id: collection.pluck(:milestone_id))
if assignee_id.present? && !assignee_id.to_i.zero?
@assignee = @assignees.find_by(id: assignee_id)
end
if author_id.present? && !author_id.to_i.zero?
@author = @authors.find_by(id: author_id)
end
if milestone_id.present? && !milestone_id.to_i.zero?
@milestone = @milestones.find_by(id: milestone_id)
end
end
def get_issues_collection
set_filters_params
issues = IssuesFinder.new.execute(current_user, @filter_params)
set_filter_values(issues)
issues
end
def get_merge_requests_collection
set_filters_params
merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params)
set_filter_values(merge_requests)
merge_requests
end
end
......@@ -3,8 +3,6 @@ class DashboardController < ApplicationController
before_filter :load_projects, except: [:projects]
before_filter :event_filter, only: :show
before_filter :default_filter, only: [:issues, :merge_requests]
def show
# Fetch only 30 projects.
......@@ -55,13 +53,13 @@ class DashboardController < ApplicationController
end
def merge_requests
@merge_requests = MergeRequestsFinder.new.execute(current_user, params)
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = IssuesFinder.new.execute(current_user, params)
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project)
......@@ -76,10 +74,4 @@ class DashboardController < ApplicationController
def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
end
def default_filter
params[:scope] = 'assigned-to-me' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
params[:authorized_only] = true
end
end
......@@ -11,8 +11,6 @@ class GroupsController < ApplicationController
# Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update]
before_filter :default_filter, only: [:issues, :merge_requests]
layout :determine_layout
before_filter :set_title, only: [:new, :create]
......@@ -23,7 +21,7 @@ class GroupsController < ApplicationController
def create
@group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name
@group.name = @group.path.dup unless @group.name
if @group.save
@group.add_owner(current_user)
......@@ -47,13 +45,13 @@ class GroupsController < ApplicationController
end
def merge_requests
@merge_requests = MergeRequestsFinder.new.execute(current_user, params)
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
@issues = IssuesFinder.new.execute(current_user, params)
@issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project)
......@@ -148,18 +146,6 @@ class GroupsController < ApplicationController
end
end
def default_filter
if params[:scope].blank?
if current_user
params[:scope] = 'assigned-to-me'
else
params[:scope] = 'all'
end
end
params[:state] = 'opened' if params[:state].blank?
params[:group_id] = @group.id
end
def group_params
params.require(:group).permit(:name, :description, :path, :avatar)
end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_filter :authenticate_user!
layout "profile"
def index
head :forbidden and return
end
def create
@application = Doorkeeper::Application.new(application_params)
if Doorkeeper.configuration.confirm_application_owner?
@application.owner = current_user
end
if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to oauth_application_url(@application)
else
render :new
end
end
def destroy
if @application.destroy
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy])
end
redirect_to applications_profile_url
end
private
def set_application
@application = current_user.oauth_applications.find(params[:id])
end
rescue_from ActiveRecord::RecordNotFound do |exception|
render "errors/not_found", layout: "errors", status: 404
end
end
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_filter :authenticate_resource_owner!
layout "profile"
def new
if pre_auth.authorizable?
if skip_authorization? || matching_token?
auth = authorization.authorize
redirect_to auth.redirect_uri
else
render "doorkeeper/authorizations/new"
end
else
render "doorkeeper/authorizations/error"
end
end
# TODO: Handle raise invalid authorization
def create
redirect_or_render authorization.authorize
end
def destroy
redirect_or_render authorization.deny
end
private
def matching_token?
Doorkeeper::AccessToken.matching_token_for(pre_auth.client,
current_resource_owner.id,
pre_auth.scopes)
end
def redirect_or_render(auth)
if auth.redirectable?
redirect_to auth.redirect_uri
else
render json: auth.body, status: auth.status
end
end
def pre_auth
@pre_auth ||=
Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration,
server.client_via_uid,
params)
end
def authorization
@authorization ||= strategy.request
end
def strategy
@strategy ||= server.authorization_request(pre_auth.response_type)
end
end
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
layout "profile"
def destroy
Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
redirect_to applications_profile_url, notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy])
end
end
......@@ -13,6 +13,11 @@ class ProfilesController < ApplicationController
def design
end
def applications
@applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens
end
def update
user_params.except!(:email) if @user.ldap_user?
......
......@@ -29,31 +29,4 @@ class Projects::ApplicationController < ApplicationController
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end
end
def set_filter_variables(collection)
params[:sort] ||= 'newest'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@sort = params[:sort].humanize
assignee_id = params[:assignee_id]
author_id = params[:author_id]
milestone_id = params[:milestone_id]
if assignee_id.present? && !assignee_id.to_i.zero?
@assignee = @project.team.find(assignee_id)
end
if author_id.present? && !author_id.to_i.zero?
@author = @project.team.find(assignee_id)
end
if milestone_id.present? && !milestone_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id)
end
@assignees = User.where(id: collection.pluck(:assignee_id))
@authors = User.where(id: collection.pluck(:author_id))
end
end
......@@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController
def index
terms = params['issue_search']
set_filter_variables(@project.issues)
@issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
@issues = get_issues_collection
@issues = @issues.full_search(terms) if terms.present?
@issues = @issues.page(params[:page]).per(20)
......
......@@ -17,9 +17,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index
set_filter_variables(@project.merge_requests)
@merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
@merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
end
......
......@@ -11,7 +11,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html
def index
@milestones = case params[:f]
@milestones = case params[:state]
when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date ASC")
......
......@@ -15,6 +15,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
redirect_to project_protected_branches_path(@project)
end
def update
protected_branch = @project.protected_branches.find(params[:id])
if protected_branch &&
protected_branch.update_attributes(
developers_can_push: params[:developers_can_push]
)
respond_to do |format|
format.json { render :json => protected_branch, status: :ok }
end
else
respond_to do |format|
format.json { render json: protected_branch.errors, status: :unprocessable_entity }
end
end
end
def destroy
@project.protected_branches.find(params[:id]).destroy
......@@ -27,6 +45,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private
def protected_branch_params
params.require(:protected_branch).permit(:name)
params.require(:protected_branch).permit(:name, :developers_can_push)
end
end
......@@ -42,7 +42,7 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server
:build_key, :server, :teamcity_url, :build_type
)
end
end
......@@ -44,6 +44,9 @@ class ProjectsController < ApplicationController
def transfer
::Projects::TransferService.new(project, current_user, project_params).execute
if @project.errors[:namespace_id].present?
flash[:alert] = @project.errors[:namespace_id].first
end
end
def show
......
......@@ -114,6 +114,10 @@ module ApplicationHelper
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end
def theme_type
Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
......@@ -271,4 +275,39 @@ module ApplicationHelper
def promo_url
'https://' + promo_host
end
def page_filter_path(options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
assignee_id: params[:assignee_id],
author_id: params[:author_id],
sort: params[:sort],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def outdated_browser?
browser.ie? && browser.version.to_i < 10
end
def path_to_key(key, admin = false)
if admin
admin_user_key_path(@user, key)
else
profile_key_path(key)
end
end
def redirect_from_root?
request.env['rack.session']['user_return_to'] ==
'/'
end
end
module DashboardHelper
def filter_path(entity, options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
project_id: params[:project_id],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def entities_per_project(project, entity)
case entity.to_sym
when :issue then @issues.where(project_id: project.id)
when :merge_request then @merge_requests.where(target_project_id: project.id)
else
[]
end.count
end
def projects_dashboard_filter_path(options={})
exist_opts = {
sort: params[:sort],
scope: params[:scope],
group: params[:group],
tag: params[:tag],
visibility_level: params[:visibility_level],
}
options = exist_opts.merge(options)
......@@ -36,32 +15,11 @@ module DashboardHelper
path
end
def assigned_entities_count(current_user, entity, scope = nil)
items = current_user.send('assigned_' + entity.pluralize)
get_count(items, scope)
def assigned_issues_dashboard_path
issues_dashboard_path(assignee_id: current_user.id)
end
def authored_entities_count(current_user, entity, scope = nil)
items = current_user.send(entity.pluralize)
get_count(items, scope)
end
def authorized_entities_count(current_user, entity, scope = nil)
items = entity.classify.constantize
get_count(items, scope, true, current_user)
end
protected
def get_count(items, scope, get_authorized = false, current_user = nil)
items = items.opened
if scope.kind_of?(Group)
items = items.of_group(scope)
elsif scope.kind_of?(Project)
items = items.of_projects(scope)
elsif get_authorized
items = items.of_projects(current_user.authorized_projects)
end
items.count
def assigned_mrs_dashboard_path
merge_requests_dashboard_path(assignee_id: current_user.id)
end
end
......@@ -117,4 +117,22 @@ module DiffHelper
[comments_left, comments_right]
end
def inline_diff_btn
params_copy = params.dup
params_copy[:view] = 'inline'
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do
'Inline'
end
end
def parallel_diff_btn
params_copy = params.dup
params_copy[:view] = 'parallel'
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do
'Side-by-side'
end
end
end
......@@ -21,15 +21,14 @@ module EventsHelper
def event_filter_link(key, tooltip)
key = key.to_s
inactive = if @event_filter.active? key
nil
else
'inactive'
end
active = if @event_filter.active? key
'active'
end
content_tag :div, class: "filter_icon #{inactive}" do
content_tag :li, class: "filter_icon #{active}" do
link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do
content_tag :i, nil, class: icon_for_event[key]
content_tag(:i, nil, class: icon_for_event[key]) +
content_tag(:span, ' ' + tooltip)
end
end
end
......
......@@ -6,7 +6,7 @@ module GroupsHelper
def leave_group_message(group)
"Are you sure you want to leave \"#{group}\" group?"
end
def should_user_see_group_roles?(user, group)
if user
user.is_admin? || group.members.exists?(user_id: user.id)
......@@ -33,15 +33,11 @@ module GroupsHelper
title
end
def group_filter_path(entity, options={})
exist_opts = {
status: params[:status]
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
def group_settings_page?
if current_controller?('groups')
current_action?('edit') || current_action?('projects')
else
false
end
end
end
module MilestonesHelper
def milestones_filter_path(opts = {})
if @project
project_milestones_path(@project, opts)
elsif @group
group_milestones_path(@group, opts)
end
end
end
......@@ -52,8 +52,11 @@ module NotesHelper
discussion_id: discussion_id
}
button_tag '', class: 'btn add-diff-note js-add-diff-note-button',
data: data, title: 'Add a comment to this line'
button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data,
title: 'Add a comment to this line') do
content_tag :i, nil, class: 'fa fa-comment-o'
end
end
def link_to_reply_diff(note)
......
......@@ -68,48 +68,6 @@ module ProjectsHelper
project_nav_tabs.include? name
end
def selected_label?(label_name)
params[:label_name].to_s.split(',').include?(label_name)
end
def labels_filter_path(label_name)
label_name =
if selected_label?(label_name)
params[:label_name].split(',').reject { |l| l == label_name }.join(',')
elsif params[:label_name].present?
"#{params[:label_name]},#{label_name}"
else
label_name
end
project_filter_path(label_name: label_name)
end
def label_filter_class(label_name)
if selected_label?(label_name)
'label-filter-item active'
else
'label-filter-item light'
end
end
def project_filter_path(options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
assignee_id: params[:assignee_id],
sort: params[:sort],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def project_active_milestones
@project.milestones.active.order("due_date, title ASC")
end
......
......@@ -17,4 +17,13 @@ module SelectsHelper
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end
def groups_select_tag(id, opts = {})
css_class = "ajax-groups-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
end
......@@ -28,6 +28,10 @@ module TabHelper
# nav_link(controller: [:tree, :refs]) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Several paths
# nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Shorthand path
# nav_link(path: 'tree#show') { "Hello" }
# # => '<li class="active">Hello</li>'
......@@ -38,25 +42,7 @@ module TabHelper
#
# Returns a list item element String
def nav_link(options = {}, &block)
if path = options.delete(:path)
if path.respond_to?(:each)
c = path.map { |p| p.split('#').first }
a = path.map { |p| p.split('#').last }
else
c, a, _ = path.split('#')
end
else
c = options.delete(:controller)
a = options.delete(:action)
end
if c && a
# When given both options, make sure BOTH are active
klass = current_controller?(*c) && current_action?(*a) ? 'active' : ''
else
# Otherwise check EITHER option
klass = current_controller?(*c) || current_action?(*a) ? 'active' : ''
end
klass = active_nav_link?(options) ? 'active' : ''
# Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key
......@@ -72,6 +58,34 @@ module TabHelper
end
end
def active_nav_link?(options)
if path = options.delete(:path)
unless path.respond_to?(:each)
path = [path]
end
path.any? do |single_path|
current_path?(single_path)
end
else
c = options.delete(:controller)
a = options.delete(:action)
if c && a
# When given both options, make sure BOTH are true
current_controller?(*c) && current_action?(*a)
else
# Otherwise check EITHER option
current_controller?(*c) || current_action?(*a)
end
end
end
def current_path?(path)
c, a, _ = path.split('#')
current_controller?(c) && current_action?(a)
end
def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
......
......@@ -53,13 +53,41 @@ module TreeHelper
File.join(*args)
end
def allowed_tree_edit?
return false unless @repository.branch_names.include?(@ref)
def allowed_tree_edit?(project = nil, ref = nil)
project ||= @project
ref ||= @ref
return false unless project.repository.branch_names.include?(ref)
if @project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, @project)
if project.protected_branch? ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, @project)
can?(current_user, :push_code, project)
end
end
def edit_blob_link(project, ref, path, options = {})
blob =
begin
project.repository.blob_at(ref, path)
rescue
nil
end
if blob && blob.text?
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
if allowed_tree_edit?(project, ref)
link_to text, project_edit_tree_path(project, tree_join(ref, path),
link_opts), class: cls
else
content_tag :span, text, class: cls + ' disabled'
end + after.html_safe
else
''
end
end
......
......@@ -174,7 +174,7 @@ class Event < ActiveRecord::Base
def valid_push?
data[:ref] && ref_name.present?
rescue => ex
rescue
false
end
......
......@@ -21,7 +21,7 @@ class Group < Namespace
has_many :users, through: :group_members
validate :avatar_type, if: ->(user) { user.avatar_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
mount_uploader :avatar, AttachmentUploader
......
......@@ -48,7 +48,7 @@ class WebHook < ActiveRecord::Base
verify: false,
basic_auth: auth)
end
rescue SocketError, Errno::ECONNREFUSED => e
rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}")
false
end
......
......@@ -189,7 +189,9 @@ class MergeRequest < ActiveRecord::Base
end
def automerge!(current_user, commit_message = nil)
MergeRequests::AutoMergeService.new.execute(self, current_user, commit_message)
MergeRequests::AutoMergeService.
new(target_project, current_user).
execute(self, commit_message)
end
def open?
......
......@@ -502,6 +502,6 @@ class Note < ActiveRecord::Base
end
def editable?
!system
!read_attribute(:system)
end
end
......@@ -6,12 +6,13 @@ class Notification
N_PARTICIPATING = 1
N_WATCH = 2
N_GLOBAL = 3
N_MENTION = 4
attr_accessor :target
class << self
def notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH]
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION]
end
def options_with_labels
......@@ -19,12 +20,13 @@ class Notification
disabled: N_DISABLED,
participating: N_PARTICIPATING,
watch: N_WATCH,
mention: N_MENTION,
global: N_GLOBAL
}
end
def project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL]
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION]
end
end
......@@ -48,6 +50,10 @@ class Notification
target.notification_level == N_GLOBAL
end
def mention?
target.notification_level == N_MENTION
end
def level
target.notification_level
end
......
......@@ -66,6 +66,7 @@ class Project < ActiveRecord::Base
has_one :slack_service, dependent: :destroy
has_one :buildbox_service, dependent: :destroy
has_one :bamboo_service, dependent: :destroy
has_one :teamcity_service, dependent: :destroy
has_one :pushover_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
......@@ -314,7 +315,8 @@ class Project < ActiveRecord::Base
end
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox bamboo)
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla
emails_on_push gemnasium slack pushover buildbox bamboo teamcity)
end
def gitlab_ci?
......@@ -329,11 +331,6 @@ class Project < ActiveRecord::Base
@ci_service ||= ci_services.select(&:activated?).first
end
# For compatibility with old code
def code
path
end
def items_for(entity)
case entity
when 'issue' then
......@@ -470,6 +467,10 @@ class Project < ActiveRecord::Base
protected_branches_names.include?(branch_name)
end
def developers_can_push_to_protected_branch?(branch_name)
protected_branches.any? { |pb| pb.name == branch_name && pb.developers_can_push }
end
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
......
......@@ -35,7 +35,7 @@ class HipchatService < Service
{ type: 'text', name: 'token', placeholder: '' },
{ type: 'text', name: 'room', placeholder: '' },
{ type: 'text', name: 'server',
placeholder: 'Leave blank for default. https://chat.hipchat.com' }
placeholder: 'Leave blank for default. https://hipchat.example.com' }
]
end
......@@ -47,7 +47,7 @@ class HipchatService < Service
def gate
options = { api_version: 'v2' }
options[:server_url] = server unless server.nil?
options[:server_url] = server unless server.blank?
@gate ||= HipChat::Client.new(token, options)
end
......
require 'slack-notifier'
class SlackMessage
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def initialize(params)
@after = params.fetch(:after)
@before = params.fetch(:before)
......@@ -23,14 +31,6 @@ class SlackMessage
private
attr_reader :after
attr_reader :before
attr_reader :commits
attr_reader :project_name
attr_reader :project_url
attr_reader :ref
attr_reader :username
def message
if new_branch?
new_branch_message
......
class TeamcityService < CiService
include HTTParty
prop_accessor :teamcity_url, :build_type, :username, :password
validates :teamcity_url, presence: true,
format: { with: URI::regexp }, if: :activated?
validates :build_type, presence: true, if: :activated?
validates :username, presence: true,
if: ->(service) { service.password? }, if: :activated?
validates :password, presence: true,
if: ->(service) { service.username? }, if: :activated?
attr_accessor :response
after_save :compose_service_hook, if: :activated?
def compose_service_hook
hook = service_hook || build_service_hook
hook.save
end
def title
'JetBrains TeamCity CI'
end
def description
'A continuous integration and build server'
end
def help
'The build configuration in Teamcity must use the build format '\
'number %build.vcs.number% '\
'you will also want to configure monitoring of all branches so merge '\
'requests build, that setting is in the vsc root advanced settings.'
end
def to_param
'teamcity'
end
def fields
[
{ type: 'text', name: 'teamcity_url',
placeholder: 'TeamCity root URL like https://teamcity.example.com' },
{ type: 'text', name: 'build_type',
placeholder: 'Build configuration ID' },
{ type: 'text', name: 'username',
placeholder: 'A user with permissions to trigger a manual build' },
{ type: 'password', name: 'password' },
]
end
def build_info(sha)
url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
"branch:unspecified:any,number:#{sha}")
auth = {
username: username,
password: password,
}
@response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
end
def build_page(sha)
build_info(sha) if @response.nil? || !@response.code
if @response.code != 200
# If actual build link can't be determined,
# send user to build summary page.
"#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
else
# If actual build link is available, go to build result page.
built_id = @response['build']['id']
"#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
"&buildTypeId=#{build_type}"
end
end
def commit_status(sha)
build_info(sha) if @response.nil? || !@response.code
return :error unless @response.code == 200 || @response.code == 404
status = if @response.code == 404
'Pending'
else
@response['build']['status']
end
if status.include?('SUCCESS')
'success'
elsif status.include?('FAILURE')
'failed'
elsif status.include?('Pending')
'pending'
else
:error
end
end
def execute(data)
auth = {
username: username,
password: password,
}
branch = data[:ref]
self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
body: "<build branchName=\"#{branch}\">"\
"<buildType id=\"#{build_type}\"/>"\
'</build>',
headers: { 'Content-type' => 'application/xml' },
basic_auth: auth
)
end
end
......@@ -106,6 +106,7 @@ class User < ActiveRecord::Base
has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event"
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
#
......@@ -564,4 +565,8 @@ class User < ActiveRecord::Base
namespaces += masters_groups
end
end
def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end
end
......@@ -5,15 +5,16 @@ module MergeRequests
# mark merge request as merged and execute all hooks and notifications
# Called when you do merge via GitLab UI
class AutoMergeService < BaseMergeService
def execute(merge_request, current_user, commit_message)
def execute(merge_request, commit_message)
merge_request.lock_mr
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
notification.merge_mr(merge_request, current_user)
notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request)
create_note(merge_request)
execute_hooks(merge_request)
true
else
......
module MergeRequests
class BaseMergeService
class BaseMergeService < MergeRequests::BaseService
private
def notification
NotificationService.new
end
def create_merge_event(merge_request, current_user)
EventCreateService.new.merge_mr(merge_request, current_user)
end
def execute_project_hooks(merge_request)
if merge_request.project
hook_data = merge_request.to_hook_data(current_user)
merge_request.project.execute_hooks(hook_data, :merge_request_hooks)
end
end
end
end
......@@ -13,7 +13,7 @@ module MergeRequests
merge_request.target_branch ||= merge_request.target_project.default_branch
unless merge_request.target_branch && merge_request.source_branch
return build_failed(merge_request, "You must select source and target branches")
return build_failed(merge_request, nil)
end
# Generate suggested MR title based on source branch name
......@@ -59,7 +59,7 @@ module MergeRequests
end
def build_failed(merge_request, message)
merge_request.errors.add(:base, message)
merge_request.errors.add(:base, message) unless message.nil?
merge_request.compare_commits = []
merge_request.can_be_created = false
merge_request
......
......@@ -6,12 +6,13 @@ module MergeRequests
# Called when you do merge via command line and push code
# to target branch
class MergeService < BaseMergeService
def execute(merge_request, current_user, commit_message)
def execute(merge_request, commit_message)
merge_request.merge
notification.merge_mr(merge_request, current_user)
notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
execute_project_hooks(merge_request)
create_note(merge_request)
execute_hooks(merge_request)
true
rescue
......
......@@ -32,7 +32,9 @@ module MergeRequests
merge_requests.uniq.select(&:source_project).each do |merge_request|
MergeRequests::MergeService.new.execute(merge_request, @current_user, nil)
MergeRequests::MergeService.
new(merge_request.target_project, @current_user).
execute(merge_request, nil)
end
end
......
module Notes
class UpdateService < BaseService
def execute
note = project.notes.find(params[:note_id])
note.note = params[:note]
if note.save
notification_service.new_note(note)
# Skip system notes, like status changes and cross-references.
unless note.system
event_service.leave_note(note, note.author)
# Create a cross-reference note if this Note contains GFM that
# names an issue, merge request, or commit.
note.references.each do |mentioned|
Note.create_cross_reference_note(mentioned, note.noteable,
note.author, note.project)
end
end
end
note
end
end
end
......@@ -144,6 +144,10 @@ class NotificationService
# Merge project watchers
recipients = recipients.concat(project_watchers(note.project)).compact.uniq
# Reject mention users unless mentioned in comment
recipients = reject_mention_users(recipients - note.mentioned_users, note.project)
recipients = recipients + note.mentioned_users
# Reject mutes users
recipients = reject_muted_users(recipients, note.project)
......@@ -285,13 +289,39 @@ class NotificationService
end
end
# Remove users with notification level 'Mentioned'
def reject_mention_users(users, project = nil)
users = users.to_a.compact.uniq
users.reject do |user|
next user.notification.mention? unless project
tm = project.project_members.find_by(user_id: user.id)
if !tm && project.group
tm = project.group.group_members.find_by(user_id: user.id)
end
# reject users who globally set mention notification and has no membership
next user.notification.mention? unless tm
# reject users who set mention notification in project
next true if tm.notification.mention?
# reject users who have N_MENTION in project and disabled in global settings
tm.notification.global? && user.notification.mention?
end
end
def new_resource_email(target, project, method)
if target.respond_to?(:participants)
recipients = target.participants
else
recipients = []
end
recipients = reject_muted_users(recipients, project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients.delete(target.author)
......@@ -302,6 +332,7 @@ class NotificationService
def close_resource_email(target, project, current_user, method)
recipients = reject_muted_users([target.author, target.assignee], project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients.delete(current_user)
......@@ -320,6 +351,7 @@ class NotificationService
# reject users with disabled notifications
recipients = reject_muted_users(recipients, project)
recipients = reject_mention_users(recipients, project)
# Reject me from recipients if I reassign an item
recipients.delete(current_user)
......@@ -331,6 +363,7 @@ class NotificationService
def reopen_resource_email(target, project, current_user, method, status)
recipients = reject_muted_users([target.author, target.assignee], project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients.delete(current_user)
......
module Oauth2::AccessTokenValidationService
# Results:
VALID = :valid
EXPIRED = :expired
REVOKED = :revoked
INSUFFICIENT_SCOPE = :insufficient_scope
class << self
def validate(token, scopes: [])
if token.expired?
return EXPIRED
elsif token.revoked?
return REVOKED
elsif !self.sufficent_scope?(token, scopes)
return INSUFFICIENT_SCOPE
else
return VALID
end
end
protected
# True if the token's scope is a superset of required scopes,
# or the required scopes is empty.
def sufficent_scope?(token, scopes)
if scopes.blank?
# if no any scopes required, the scopes of token is sufficient.
return true
else
# If there are scopes required, then check whether
# the set of authorized scopes is a superset of the set of required scopes
required_scopes = Set.new(scopes)
authorized_scopes = Set.new(token.scopes)
return authorized_scopes >= required_scopes
end
end
end
end
\ No newline at end of file
......@@ -12,12 +12,17 @@ module Projects
@project.visibility_level = default_features.visibility_level
end
# Parametrize path for project
#
# Ex.
# 'GitLab HQ'.parameterize => "gitlab-hq"
#
@project.path = @project.name.dup.parameterize unless @project.path.present?
# Set project name from path
if @project.name.present? && @project.path.present?
# if both name and path set - everything is ok
elsif @project.path.present?
# Set project name from path
@project.name = @project.path.dup
elsif @project.name.present?
# For compatibility - set path from name
# TODO: remove this in 8.0
@project.path = @project.name.dup.parameterize
end
# get namespace id
namespace_id = params[:namespace_id]
......
......@@ -21,17 +21,6 @@
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
- else
.form-group.group_name_holder
= f.label :path, class: 'control-label' do
%span Group path
.col-sm-10
= f.text_field :path, placeholder: "example-group", class: "form-control danger"
.bs-callout.bs-callout-danger
%ul
%li Changing group path can have unintended side effects.
%li Renaming group path will rename directory for all related projects
%li It will change web url for access group and group projects.
%li It will change the git path to repositories under this group.
.form-actions
= f.submit 'Save changes', class: "btn btn-primary"
= link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
......
= render "profiles/keys/key_details", admin: true
......@@ -20,6 +20,8 @@
%a{"data-toggle" => "tab", href: "#groups"} Groups
%li
%a{"data-toggle" => "tab", href: "#projects"} Projects
%li
%a{"data-toggle" => "tab", href: "#ssh-keys"} SSH keys
.tab-content
#account.tab-pane.active
......@@ -217,3 +219,5 @@
- if tm.respond_to? :project
= link_to project_team_member_path(project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do
%i.fa.fa-times
#ssh-keys.tab-pane
= render 'profiles/keys/key_table', admin: true
.panel.panel-default
.panel-heading.clearfix
= search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new pull-right" do
%i.fa.fa-plus
New group
.input-group
= search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_group?
.input-group-addon
= link_to new_group_path, class: "" do
%strong New group
%ul.well-list.dash-list
- groups.each do |group|
%li.group-row
......
.panel.panel-default
.panel-heading.clearfix
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_project?
= link_to new_project_path, class: "btn btn-new pull-right" do
%i.fa.fa-plus
New project
.input-group
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_project?
.input-group-addon
= link_to new_project_path, class: "" do
%strong New project
%ul.well-list.dash-list
- projects.each do |project|
......
%fieldset
%ul.nav.nav-pills.nav-stacked
= nav_tab :scope, nil do
= link_to projects_dashboard_filter_path(scope: nil) do
All
%span.pull-right
= current_user.authorized_projects.count
= nav_tab :scope, 'personal' do
= link_to projects_dashboard_filter_path(scope: 'personal') do
Personal
%span.pull-right
= current_user.personal_projects.count
= nav_tab :scope, 'joined' do
= link_to projects_dashboard_filter_path(scope: 'joined') do
Joined
%span.pull-right
= current_user.authorized_projects.joined(current_user).count
= nav_tab :scope, 'owned' do
= link_to projects_dashboard_filter_path(scope: 'owned') do
Owned
%span.pull-right
= current_user.owned_projects.count
.dash-projects-filters.append-bottom-20
.pull-left.append-right-20
%ul.nav.nav-pills.nav-compact
= nav_tab :scope, nil do
= link_to projects_dashboard_filter_path(scope: nil) do
All
= nav_tab :scope, 'personal' do
= link_to projects_dashboard_filter_path(scope: 'personal') do
Personal
= nav_tab :scope, 'joined' do
= link_to projects_dashboard_filter_path(scope: 'joined') do
Joined
= nav_tab :scope, 'owned' do
= link_to projects_dashboard_filter_path(scope: 'owned') do
Owned
%fieldset
%legend Visibility
%ul.nav.nav-pills.nav-stacked.nav-small.visibility-filter
- Gitlab::VisibilityLevel.values.each do |level|
%li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(visibility_level: level) do
= visibility_level_icon(level)
= visibility_level_label(level)
.dropdown.inline.append-right-10
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-globe
%span.light Visibility:
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to projects_dashboard_filter_path(visibility_level: nil) do
Any
- Gitlab::VisibilityLevel.values.each do |level|
%li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(visibility_level: level) do
= visibility_level_icon(level)
= visibility_level_label(level)
- if @groups.present?
%fieldset
%legend Groups
%ul.nav.nav-pills.nav-stacked.nav-small
- @groups.each do |group|
%li{ class: (group.name == params[:group]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(group: group.name) do
%i.fa.fa-folder-o
= group.name
%small.pull-right
= group.projects.count
- if @groups.present?
.dropdown.inline.append-right-10
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-group
%span.light Group:
- if params[:group].present?
= Group.find_by(name: params[:group]).name
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to projects_dashboard_filter_path(group: nil) do
Any
- @groups.each do |group|
%li{ class: (group.name == params[:group]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(group: group.name) do
= group.name
%small.pull-right
= group.projects.count
- if @tags.present?
%fieldset
%legend Tags
%ul.nav.nav-pills.nav-stacked.nav-small
- @tags.each do |tag|
%li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(scope: params[:scope], tag: tag.name) do
%i.fa.fa-tag
= tag.name
- if @tags.present?
.dropdown.inline.append-right-10
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-tags
%span.light Tags:
- if params[:tag].present?
= params[:tag]
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to projects_dashboard_filter_path(tag: nil) do
Any
- @tags.each do |tag|
%li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(tag: tag.name) do
%i.fa.fa-tag
= tag.name
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
= @sort.humanize
- else
Name
%b.caret
%ul.dropdown-menu
%li
= link_to projects_dashboard_filter_path(sort: nil) do
Name
= link_to projects_dashboard_filter_path(sort: 'newest') do
= sort_title_recently_created
= link_to projects_dashboard_filter_path(sort: 'oldest') do
= sort_title_oldest_created
= link_to projects_dashboard_filter_path(sort: 'recently_updated') do
= sort_title_recently_updated
= link_to projects_dashboard_filter_path(sort: 'last_updated') do
= sort_title_oldest_updated
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100755 to 100644
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