Commit bdac5ddd authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

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

CE changes from master

Sidebar navigation, OAuth and other changes from CE

See merge request !275
parents 4f0d2d33 01f7734f
v 7.7.0
-
-
-
-
-
-
-
-
- OAuth applications feature
-
-
- Set project path instead of project name in create form
-
-
- New side navigation
v 7.6.0 v 7.6.0
- Fork repository to groups - Fork repository to groups
- New rugged version - New rugged version
......
...@@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps. ...@@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps.
1. Community questions answered 1. Community questions answered
1. Answers to questions radiated (in docs/wiki/etc.) 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 ## Merge request description format
1. What does this MR do? 1. What does this MR do?
...@@ -118,6 +128,7 @@ Please ensure you support the feature you contribute through all of these steps. ...@@ -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. 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. 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. 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. Keeps the GitLab code base clean and well structured
1. Contains functionality we think other users will benefit from too 1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes 1. Doesn't add configuration options since they complicate future changes
......
...@@ -29,6 +29,8 @@ gem 'omniauth-twitter' ...@@ -29,6 +29,8 @@ gem 'omniauth-twitter'
gem 'omniauth-github' gem 'omniauth-github'
gem 'omniauth-shibboleth' gem 'omniauth-shibboleth'
gem 'omniauth-kerberos' gem 'omniauth-kerberos'
gem 'doorkeeper', '2.0.1'
gem "rack-oauth2", "~> 1.0.5"
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
......
...@@ -37,6 +37,7 @@ GEM ...@@ -37,6 +37,7 @@ GEM
rake (>= 0.8.7) rake (>= 0.8.7)
arel (5.0.1.20140414130214) arel (5.0.1.20140414130214)
asciidoctor (0.1.4) asciidoctor (0.1.4)
attr_required (1.0.0)
awesome_print (1.2.0) awesome_print (1.2.0)
axiom-types (0.0.5) axiom-types (0.0.5)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
...@@ -107,6 +108,8 @@ GEM ...@@ -107,6 +108,8 @@ GEM
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.3) diffy (3.0.3)
docile (1.1.5) docile (1.1.5)
doorkeeper (2.0.1)
railties (>= 3.1)
dotenv (0.9.0) dotenv (0.9.0)
dropzonejs-rails (0.4.14) dropzonejs-rails (0.4.14)
rails (> 3.1) rails (> 3.1)
...@@ -158,7 +161,7 @@ GEM ...@@ -158,7 +161,7 @@ GEM
dotenv (>= 0.7) dotenv (>= 0.7)
thor (>= 0.13.6) thor (>= 0.13.6)
formatador (0.2.4) formatador (0.2.4)
gemnasium-gitlab-service (0.2.2) gemnasium-gitlab-service (0.2.3)
rugged (~> 0.19) rugged (~> 0.19)
gherkin-ruby (0.3.1) gherkin-ruby (0.3.1)
racc racc
...@@ -250,6 +253,7 @@ GEM ...@@ -250,6 +253,7 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpauth (0.2.1) httpauth (0.2.1)
httpclient (2.5.3.3)
i18n (0.6.11) i18n (0.6.11)
ice_cube (0.12.1) ice_cube (0.12.1)
ice_nine (0.10.0) ice_nine (0.10.0)
...@@ -369,6 +373,12 @@ GEM ...@@ -369,6 +373,12 @@ GEM
rack (>= 1.1.3) rack (>= 1.1.3)
rack-mount (0.8.3) rack-mount (0.8.3)
rack (>= 1.0.0) 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-protection (1.5.1)
rack rack
rack-test (0.6.2) rack-test (0.6.2)
...@@ -621,6 +631,7 @@ DEPENDENCIES ...@@ -621,6 +631,7 @@ DEPENDENCIES
devise (= 3.2.4) devise (= 3.2.4)
devise-async (= 0.9.0) devise-async (= 0.9.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (= 2.0.1)
dropzonejs-rails dropzonejs-rails
email_spec email_spec
enumerize enumerize
...@@ -678,6 +689,7 @@ DEPENDENCIES ...@@ -678,6 +689,7 @@ DEPENDENCIES
rack-attack rack-attack
rack-cors rack-cors
rack-mini-profiler rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0) rails (~> 4.1.0)
rails_autolink (~> 1.1) rails_autolink (~> 1.1)
rails_best_practices rails_best_practices
......
...@@ -76,69 +76,30 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ...@@ -76,69 +76,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. 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). 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 ## Third-party applications
There are a lot of applications and API wrappers for GitLab. There are a lot of applications and API wrappers for GitLab.
Find them [on our website](https://about.gitlab.com/applications/). 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). 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). 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 ## Install a development environment
We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). 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 cp config/unicorn.rb.example.development config/unicorn.rb
## Run in development mode 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).
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`
## Documentation ## Documentation
......
...@@ -12,7 +12,7 @@ class @Activities ...@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) -> toggleFilter: (sender) ->
sender.parent().toggleClass "inactive" sender.parent().toggleClass "active"
event_filters = $.cookie("event_filter") event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0] filter = sender.attr("id").split("_")[0]
if event_filters if event_filters
......
...@@ -51,12 +51,6 @@ window.ajaxGet = (url) -> ...@@ -51,12 +51,6 @@ window.ajaxGet = (url) ->
window.showAndHide = (selector) -> window.showAndHide = (selector) ->
window.errorMessage = (message) ->
ehtml = $("<p>")
ehtml.addClass("error_message")
ehtml.html(message)
ehtml
window.split = (val) -> window.split = (val) ->
return val.split( /,\s*/ ) return val.split( /,\s*/ )
......
class @Issue class @Issue
constructor: -> constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide() $('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", -> $(".context .inline-update").on "change", "select", ->
$(this).submit() $(this).submit()
$(".issue-box .inline-update").on "change", "#issue_assignee_id", -> $(".context .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit() $(this).submit()
if $("a.btn-close").length if $("a.btn-close").length
......
...@@ -26,9 +26,9 @@ class @MergeRequest ...@@ -26,9 +26,9 @@ class @MergeRequest
initContextWidget: -> initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide() $('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", -> $(".context .inline-update").on "change", "select", ->
$(this).submit() $(this).submit()
$(".issue-box .inline-update").on "change", "#merge_request_assignee_id", -> $(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit() $(this).submit()
initMergeWidget: -> initMergeWidget: ->
...@@ -89,6 +89,9 @@ class @MergeRequest ...@@ -89,6 +89,9 @@ class @MergeRequest
this.$('.merge-request-tabs .diffs-tab').addClass 'active' this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded this.loadDiff() unless @diffs_loaded
this.$('.diffs').show() this.$('.diffs').show()
when 'commits'
this.$('.merge-request-tabs .commits-tab').addClass 'active'
this.$('.commits').show()
else else
this.$('.merge-request-tabs .notes-tab').addClass 'active' this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show() this.$('.notes').show()
......
...@@ -375,7 +375,7 @@ class @Notes ...@@ -375,7 +375,7 @@ class @Notes
### ###
addDiffNote: (e) => addDiffNote: (e) =>
e.preventDefault() e.preventDefault()
link = e.target link = e.currentTarget
form = $(".js-new-note-form") form = $(".js-new-note-form")
row = $(link).closest("tr") row = $(link).closest("tr")
nextRow = row.next() nextRow = row.next()
......
...@@ -46,7 +46,7 @@ class @ContributorsGraph ...@@ -46,7 +46,7 @@ class @ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width() - 70 @width = $('.container').width() - 345
@height = 200 @height = 200
@x = null @x = null
@y = null @y = null
...@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph ...@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width()/2 - 100 @width = $('.container').width()/2 - 225
@height = 200 @height = 200
@x = null @x = null
@y = null @y = null
......
/** /**
* Issue box: * Issue box for showing Open/Closed state:
* Huge block (one per page) for storing title, descripion and other information.
* Used for Issue#show page, MergeRequest#show page etc * Used for Issue#show page, MergeRequest#show page etc
* *
* CLasses:
* .issue-box - Regular box
*/ */
.issue-box { .issue-box {
color: #555; display: inline-block;
margin:20px 0; padding: 7px 13px;
background: $box_bg; font-weight: normal;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); margin-right: 5px;
&.issue-box-closed { &.issue-box-closed {
.state {
background-color: #F3CECE;
border-color: $border_danger;
}
.state-label {
background-color: $bg_danger; background-color: $bg_danger;
color: #FFF; color: #FFF;
} }
}
&.issue-box-merged { &.issue-box-merged {
.state {
background-color: #B7CEE7;
border-color: $border_primary;
}
.state-label {
background-color: $bg_primary; background-color: $bg_primary;
color: #FFF; color: #FFF;
} }
}
&.issue-box-open { &.issue-box-open {
.state {
background-color: #D6F1D7;
border-color: $bg_success;
}
.state-label {
background-color: $bg_success; background-color: $bg_success;
color: #FFF; color: #FFF;
} }
}
&.issue-box-expired { &.issue-box-expired {
.state {
background-color: #EEE9B3;
border-color: #faebcc;
}
.state-label {
background: #cea61b; background: #cea61b;
color: #FFF; 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;
}
}
} }
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 @@ ...@@ -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) { @media (max-width: $screen-xs-max) {
......
...@@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary; ...@@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary;
color: #666; color: #666;
} }
.nav-compact > li > a {
padding: 6px 12px;
}
.nav-small > li > a { .nav-small > li > a {
padding: 3px 5px; padding: 3px 5px;
font-size: 12px; font-size: 12px;
......
...@@ -4,10 +4,6 @@ html { ...@@ -4,10 +4,6 @@ html {
&.touch .tooltip { display: none !important; } &.touch .tooltip { display: none !important; }
} }
body {
padding-bottom: 20px;
}
.container { .container {
padding-top: 0; padding-top: 0;
z-index: 5; z-index: 5;
......
...@@ -44,6 +44,6 @@ $added: #63c363; ...@@ -44,6 +44,6 @@ $added: #63c363;
$deleted: #f77; $deleted: #f77;
/** /**
* * NProgress customize
*/ */
$nprogress-color: #3498db; $nprogress-color: #3498db;
...@@ -23,20 +23,6 @@ ...@@ -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 { .dash-sidebar-tabs {
margin-bottom: 2px; margin-bottom: 2px;
border: none; border: none;
......
...@@ -140,36 +140,6 @@ ...@@ -140,36 +140,6 @@
} }
} }
/**
* 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 * Last push widget
...@@ -203,3 +173,7 @@ ...@@ -203,3 +173,7 @@
} }
} }
} }
.event_filter li a {
padding: 5px 10px;
}
...@@ -52,8 +52,6 @@ header { ...@@ -52,8 +52,6 @@ header {
border-width: 0; border-width: 0;
font-size: 18px; font-size: 18px;
.app_logo { margin-left: -15px; }
.title { .title {
@include str-truncated(70%); @include str-truncated(70%);
} }
...@@ -86,6 +84,11 @@ header { ...@@ -86,6 +84,11 @@ header {
z-index: 10; z-index: 10;
.container {
width: 100% !important;
padding-left: 0px;
}
/** /**
* *
* Logo holder * Logo holder
...@@ -232,21 +235,6 @@ header { ...@@ -232,21 +235,6 @@ header {
color: #fff; 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 { .search .search-input {
......
...@@ -162,3 +162,7 @@ form.edit-issue { ...@@ -162,3 +162,7 @@ form.edit-issue {
} }
} }
} }
.issue-title {
margin-top: 0;
}
...@@ -19,17 +19,15 @@ ...@@ -19,17 +19,15 @@
} }
} }
.merge-request .merge-request-tabs{ @media(min-width: $screen-sm-max) {
border-bottom: 2px solid $border_primary; .merge-request .merge-request-tabs{
margin: 20px 0; margin: 20px 0;
li { li {
a { a {
padding: 15px 40px; padding: 15px 40px;
font-size: 14px; font-size: 14px;
margin-bottom: -2px; }
border-bottom: 2px solid $border_primary;
@include border-radius(0px);
} }
} }
} }
...@@ -106,6 +104,7 @@ ...@@ -106,6 +104,7 @@
.mr-state-widget { .mr-state-widget {
background: $box_bg; background: $box_bg;
margin-bottom: 20px; margin-bottom: 20px;
color: #666;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget { .ci_widget {
...@@ -150,7 +149,6 @@ ...@@ -150,7 +149,6 @@
padding: 10px 15px; padding: 10px 15px;
h4 { h4 {
font-size: 20px;
font-weight: normal; font-weight: normal;
} }
......
.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; }
}
}
}
}
}
}
...@@ -155,19 +155,26 @@ ul.notes { ...@@ -155,19 +155,26 @@ ul.notes {
} }
.add-diff-note { .add-diff-note {
background: image-url("diff_note_add.png") no-repeat left 0; margin-top: -4px;
border: none; @include border-radius(40px);
height: 22px; background: #FFF;
margin-left: -65px; padding: 4px;
font-size: 16px;
color: $link_color;
margin-left: -60px;
position: absolute; position: absolute;
width: 22px;
z-index: 10; z-index: 10;
transition: all 0.2s ease;
// "hide" it by default // "hide" it by default
opacity: 0.0; opacity: 0.0;
filter: alpha(opacity=0); filter: alpha(opacity=0);
&:hover { &:hover {
font-size: 24px;
background: $bg_primary;
color: #FFF;
@include show-add-diff-note; @include show-add-diff-note;
} }
} }
......
.page-with-sidebar {
background: #F5F5F5;
}
.sidebar-wrapper {
overflow-y: auto;
background: #F5F5F5;
}
.content-wrapper {
width: 100%;
padding: 15px;
background: #FFF;
}
.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 {
line-height: 28px;
font-size: 12px;
list-style: none;
a {
padding: 5px 15px;
font-size: 12px;
padding-left: 20px;
}
}
}
@mixin expanded-sidebar {
.page-with-sidebar {
padding-left: 250px;
}
.sidebar-wrapper {
width: 250px;
position: absolute;
left: 250px;
height: 100%;
margin-left: -250px;
.nav-sidebar {
margin-top: 20px;
position: fixed;
top: 45px;
width: 250px;
}
}
.content-wrapper {
padding: 20px;
border-left: 1px solid #EAEAEA;
}
}
@mixin folded-sidebar {
.page-with-sidebar {
padding-left: 50px;
}
.sidebar-wrapper {
width: 52px;
position: absolute;
left: 50px;
height: 100%;
margin-left: -50px;
.nav-sidebar {
margin-top: 20px;
position: fixed;
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 @@ ...@@ -17,19 +17,6 @@
@include border-radius(0); @include border-radius(0);
tr { 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 { &:hover {
td { td {
background: $hover; background: $hover;
......
...@@ -37,13 +37,3 @@ ...@@ -37,13 +37,3 @@
margin: 0 8px; 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 @@ ...@@ -9,17 +9,15 @@
.navbar-inner { .navbar-inner {
background: #F1F1F1; background: #F1F1F1;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #DDD;
.app_logo {
background-color: #DDD;
}
.nav > li > a { .nav > li > a {
color: $style_color; color: $style_color;
} }
.separator {
background: #F9F9F9;
border-left: 1px solid #DDD;
}
}
} }
} }
.main-nav {
background: #FFF;
} }
} }
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #436; background-color: #436;
} }
} }
.separator { .app_logo {
background: #436; background-color: #325;
border-left: 1px solid #659;
} }
.nav > li > a { .nav > li > a {
color: #98C; color: #98C;
......
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #272727; background-color: #272727;
} }
} }
.separator { .app_logo {
background: #272727; background-color: #222;
border-left: 1px solid #474747;
} }
} }
} }
......
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #373D47; background-color: #373D47;
} }
} }
.separator { .app_logo {
background: #373D47; background-color: #24272D;
border-left: 1px solid #575D67;
} }
.nav > li > a { .nav > li > a {
color: #979DA7; color: #979DA7;
......
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #018865; background-color: #018865;
} }
} }
.separator { .app_logo {
background: #018865; background-color: #017855;
border-left: 1px solid #11A885;
} }
.nav > li > a { .nav > li > a {
color: #ADC; color: #ADC;
......
...@@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController
def create def create
@group = Group.new(group_params) @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 if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
......
...@@ -245,4 +245,67 @@ class ApplicationController < ActionController::Base ...@@ -245,4 +245,67 @@ class ApplicationController < ActionController::Base
render_404 and return render_404 and return
end end
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
unless @filter_params[:assignee_id]
@filter_params[:assignee_id] = current_user.id
end
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 end
...@@ -3,8 +3,6 @@ class DashboardController < ApplicationController ...@@ -3,8 +3,6 @@ class DashboardController < ApplicationController
before_filter :load_projects, except: [:projects] before_filter :load_projects, except: [:projects]
before_filter :event_filter, only: :show before_filter :event_filter, only: :show
before_filter :default_filter, only: [:issues, :merge_requests]
def show def show
# Fetch only 30 projects. # Fetch only 30 projects.
...@@ -55,13 +53,13 @@ class DashboardController < ApplicationController ...@@ -55,13 +53,13 @@ class DashboardController < ApplicationController
end end
def merge_requests 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.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
end end
def issues def issues
@issues = IssuesFinder.new.execute(current_user, params) @issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
...@@ -76,10 +74,4 @@ class DashboardController < ApplicationController ...@@ -76,10 +74,4 @@ class DashboardController < ApplicationController
def load_projects def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived @projects = current_user.authorized_projects.sorted_by_activity.non_archived
end 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 end
...@@ -11,8 +11,6 @@ class GroupsController < ApplicationController ...@@ -11,8 +11,6 @@ class GroupsController < ApplicationController
# Load group projects # Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update] before_filter :load_projects, except: [:new, :create, :projects, :edit, :update]
before_filter :default_filter, only: [:issues, :merge_requests]
layout :determine_layout layout :determine_layout
before_filter :set_title, only: [:new, :create] before_filter :set_title, only: [:new, :create]
...@@ -23,7 +21,7 @@ class GroupsController < ApplicationController ...@@ -23,7 +21,7 @@ class GroupsController < ApplicationController
def create def create
@group = Group.new(group_params) @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 if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
...@@ -49,13 +47,13 @@ class GroupsController < ApplicationController ...@@ -49,13 +47,13 @@ class GroupsController < ApplicationController
end end
def merge_requests 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.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
end end
def issues def issues
@issues = IssuesFinder.new.execute(current_user, params) @issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
...@@ -150,18 +148,6 @@ class GroupsController < ApplicationController ...@@ -150,18 +148,6 @@ class GroupsController < ApplicationController
end end
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 def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :membership_lock) params.require(:group).permit(:name, :description, :path, :avatar, :membership_lock)
end 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 ...@@ -13,6 +13,11 @@ class ProfilesController < ApplicationController
def design def design
end end
def applications
@applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens
end
def update def update
user_params.except!(:email) if @user.ldap_user? user_params.except!(:email) if @user.ldap_user?
......
...@@ -29,31 +29,4 @@ class Projects::ApplicationController < ApplicationController ...@@ -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" redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end end
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 end
...@@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController
def index def index
terms = params['issue_search'] terms = params['issue_search']
set_filter_variables(@project.issues) @issues = get_issues_collection
@issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
@issues = @issues.full_search(terms) if terms.present? @issues = @issues.full_search(terms) if terms.present?
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
......
...@@ -17,9 +17,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -17,9 +17,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index def index
set_filter_variables(@project.merge_requests) @merge_requests = get_merge_requests_collection
@merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
end end
......
...@@ -44,6 +44,9 @@ class ProjectsController < ApplicationController ...@@ -44,6 +44,9 @@ class ProjectsController < ApplicationController
def transfer def transfer
::Projects::TransferService.new(project, current_user, project_params).execute ::Projects::TransferService.new(project, current_user, project_params).execute
if @project.errors[:namespace_id].present?
flash[:alert] = @project.errors[:namespace_id].first
end
end end
def show def show
......
...@@ -114,6 +114,10 @@ module ApplicationHelper ...@@ -114,6 +114,10 @@ module ApplicationHelper
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id)) Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end end
def theme_type
Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
end
def user_color_scheme_class def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end end
...@@ -271,4 +275,22 @@ module ApplicationHelper ...@@ -271,4 +275,22 @@ module ApplicationHelper
def promo_url def promo_url
'https://' + promo_host 'https://' + promo_host
end 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
end end
module DashboardHelper 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) def entities_per_project(project, entity)
case entity.to_sym case entity.to_sym
when :issue then @issues.where(project_id: project.id) when :issue then @issues.where(project_id: project.id)
......
...@@ -117,4 +117,22 @@ module DiffHelper ...@@ -117,4 +117,22 @@ module DiffHelper
[comments_left, comments_right] [comments_left, comments_right]
end 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 end
...@@ -21,15 +21,14 @@ module EventsHelper ...@@ -21,15 +21,14 @@ module EventsHelper
def event_filter_link(key, tooltip) def event_filter_link(key, tooltip)
key = key.to_s key = key.to_s
inactive = if @event_filter.active? key active = if @event_filter.active? key
nil 'active'
else
'inactive'
end 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 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 end
end end
......
...@@ -44,4 +44,14 @@ module GroupsHelper ...@@ -44,4 +44,14 @@ module GroupsHelper
path << "?#{options.to_param}" path << "?#{options.to_param}"
path path
end end
def group_settings_page?
if current_controller?('groups')
current_action?('edit') || current_action?('projects')
elsif current_controller?('ldap_group_links') || current_controller?('audit_events')
true
else
false
end
end
end end
...@@ -52,8 +52,11 @@ module NotesHelper ...@@ -52,8 +52,11 @@ module NotesHelper
discussion_id: discussion_id discussion_id: discussion_id
} }
button_tag '', class: 'btn add-diff-note js-add-diff-note-button', button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data, title: 'Add a comment to this line' data: data,
title: 'Add a comment to this line') do
content_tag :i, nil, class: 'fa fa-comment-o'
end
end end
def link_to_reply_diff(note) def link_to_reply_diff(note)
......
...@@ -68,48 +68,6 @@ module ProjectsHelper ...@@ -68,48 +68,6 @@ module ProjectsHelper
project_nav_tabs.include? name project_nav_tabs.include? name
end 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 def project_active_milestones
@project.milestones.active.order("due_date, title ASC") @project.milestones.active.order("due_date, title ASC")
end end
......
...@@ -28,6 +28,10 @@ module TabHelper ...@@ -28,6 +28,10 @@ module TabHelper
# nav_link(controller: [:tree, :refs]) { "Hello" } # nav_link(controller: [:tree, :refs]) { "Hello" }
# # => '<li class="active">Hello</li>' # # => '<li class="active">Hello</li>'
# #
# # Several paths
# nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Shorthand path # # Shorthand path
# nav_link(path: 'tree#show') { "Hello" } # nav_link(path: 'tree#show') { "Hello" }
# # => '<li class="active">Hello</li>' # # => '<li class="active">Hello</li>'
...@@ -38,25 +42,7 @@ module TabHelper ...@@ -38,25 +42,7 @@ module TabHelper
# #
# Returns a list item element String # Returns a list item element String
def nav_link(options = {}, &block) def nav_link(options = {}, &block)
if path = options.delete(:path) klass = active_nav_link?(options) ? 'active' : ''
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
# Add our custom class into the html_options, which may or may not exist # 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 # and which may or may not already have a :class key
...@@ -72,6 +58,34 @@ module TabHelper ...@@ -72,6 +58,34 @@ module TabHelper
end end
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 def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project) return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
......
...@@ -53,13 +53,41 @@ module TreeHelper ...@@ -53,13 +53,41 @@ module TreeHelper
File.join(*args) File.join(*args)
end end
def allowed_tree_edit? def allowed_tree_edit?(project = nil, ref = nil)
return false unless @repository.branch_names.include?(@ref) project ||= @project
ref ||= @ref
return false unless project.repository.branch_names.include?(ref)
if @project.protected_branch? @ref if project.protected_branch? ref
can?(current_user, :push_code_to_protected_branches, @project) can?(current_user, :push_code_to_protected_branches, project)
else 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
end end
......
...@@ -106,6 +106,7 @@ class User < ActiveRecord::Base ...@@ -106,6 +106,7 @@ class User < ActiveRecord::Base
has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event" 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_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 :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
# #
...@@ -575,4 +576,8 @@ class User < ActiveRecord::Base ...@@ -575,4 +576,8 @@ class User < ActiveRecord::Base
namespaces += masters_groups namespaces += masters_groups
end end
end end
def oauth_authorized_tokens
Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
end
end end
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 ...@@ -12,12 +12,17 @@ module Projects
@project.visibility_level = default_features.visibility_level @project.visibility_level = default_features.visibility_level
end end
# Parametrize path for project # Set project name from path
# if @project.name.present? && @project.path.present?
# Ex. # if both name and path set - everything is ok
# 'GitLab HQ'.parameterize => "gitlab-hq" elsif @project.path.present?
# # Set project name from path
@project.path = @project.name.dup.parameterize unless @project.path.present? @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 # get namespace id
namespace_id = params[:namespace_id] namespace_id = params[:namespace_id]
......
...@@ -21,18 +21,6 @@ ...@@ -21,18 +21,6 @@
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
- else - 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 .form-actions
= f.submit 'Save changes', class: "btn btn-primary" = f.submit 'Save changes', class: "btn btn-primary"
= link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel" = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
......
.row %h3.page-title Group Audit Events
.col-md-2 %p.light Events in #{@group.name}
= render 'groups/settings_nav'
.col-md-10 = render 'event_table', events: @events
%h3.page-title Group Audit Events
%p.light Events in #{@group.name}
= render 'event_table', events: @events
.panel.panel-default .panel.panel-default
.panel-heading.clearfix .panel-heading.clearfix
.input-group
= search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_group? - if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new pull-right" do .input-group-addon
%i.fa.fa-plus = link_to new_group_path, class: "" do
New group %strong New group
%ul.well-list.dash-list %ul.well-list.dash-list
- groups.each do |group| - groups.each do |group|
%li.group-row %li.group-row
......
.panel.panel-default .panel.panel-default
.panel-heading.clearfix .panel-heading.clearfix
.input-group
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- if current_user.can_create_project? - if current_user.can_create_project?
= link_to new_project_path, class: "btn btn-new pull-right" do .input-group-addon
%i.fa.fa-plus = link_to new_project_path, class: "" do
New project %strong New project
%ul.well-list.dash-list %ul.well-list.dash-list
- projects.each do |project| - projects.each do |project|
......
...@@ -15,11 +15,4 @@ ...@@ -15,11 +15,4 @@
= render "groups", groups: @groups = render "groups", groups: @groups
.prepend-top-20 .prepend-top-20
%span.rss-icon = render 'shared/promo'
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
%strong
%i.fa.fa-rss
News Feed
%hr
= render 'shared/promo'
...@@ -5,10 +5,6 @@ ...@@ -5,10 +5,6 @@
List all issues from all projects you have access to. List all issues from all projects you have access to.
%hr %hr
.row .append-bottom-20
.fixed.sidebar-expand-button.hidden-lg.hidden-md = render 'shared/issuable_filter'
%i.fa.fa-list.fa-2x = render 'shared/issues'
.col-md-3.responsive-side
= render 'shared/filter', entity: 'issue'
.col-md-9
= render 'shared/issues'
...@@ -5,10 +5,6 @@ ...@@ -5,10 +5,6 @@
%p.light %p.light
List all merge requests from all projects you have access to. List all merge requests from all projects you have access to.
%hr %hr
.row .append-bottom-20
.fixed.sidebar-expand-button.hidden-lg.hidden-md = render 'shared/issuable_filter'
%i.fa.fa-list.fa-2x = render 'shared/merge_requests'
.col-md-3.responsive-side
= render 'shared/filter', entity: 'merge_request'
.col-md-9
= render 'shared/merge_requests'
...@@ -38,17 +38,19 @@ ...@@ -38,17 +38,19 @@
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
= project.name_with_namespace = project.name_with_namespace
- if project.forked_from_project
&nbsp;
%small
%i.fa.fa-code-fork
Forked from:
= link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project)
- if current_user.can_leave_project?(project) - if current_user.can_leave_project?(project)
.pull-right .pull-right
= link_to leave_project_team_members_path(project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do = link_to leave_project_team_members_path(project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do
%i.fa.fa-sign-out %i.fa.fa-sign-out
Leave Leave
- if project.forked_from_project
%small.pull-right
%i.fa.fa-code-fork
Forked from:
= link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project)
.project-info .project-info
.pull-right .pull-right
- if project.archived? - if project.archived?
......
- submit_btn_css ||= 'btn btn-link btn-remove btn-small'
= form_tag oauth_application_path(application) do
%input{:name => "_method", :type => "hidden", :value => "delete"}/
= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
\ No newline at end of file
= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f|
- if application.errors.any?
.alert.alert-danger{"data-alert" => ""}
%p Whoops! Check your form for possible errors
= content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do
= f.label :name, class: 'col-sm-2 control-label'
.col-sm-10
= f.text_field :name, class: 'form-control'
= doorkeeper_errors_for application, :name
= content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do
= f.label :redirect_uri, class: 'col-sm-2 control-label'
.col-sm-10
= f.text_area :redirect_uri, class: 'form-control'
= doorkeeper_errors_for application, :redirect_uri
%span.help-block
Use one line per URI
- if Doorkeeper.configuration.native_redirect_uri
%span.help-block
Use
%code= Doorkeeper.configuration.native_redirect_uri
for local tests
.form-actions
= f.submit 'Submit', class: "btn btn-primary wide"
= link_to "Cancel", applications_profile_path, class: "btn btn-default"
%h3.page-title Edit application
= render 'form', application: @application
\ No newline at end of file
%h3.page-title Your applications
%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
%table.table.table-striped
%thead
%tr
%th Name
%th Callback URL
%th
%th
%tbody
- @applications.each do |application|
%tr{:id => "application_#{application.id}"}
%td= link_to application.name, oauth_application_path(application)
%td= application.redirect_uri
%td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link'
%td= render 'delete_form', application: application
\ No newline at end of file
%h3.page-title New application
= render 'form', application: @application
\ No newline at end of file
%h3.page-title
Application: #{@application.name}
%table.table
%tr
%td
Application Id
%td
%code#application_id= @application.uid
%tr
%td
Secret:
%td
%code#secret= @application.secret
%tr
%td
Callback url
%td
- @application.redirect_uri.split.each do |uri|
%div
%span.monospace= uri
.form-actions
= link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left'
= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
%h3.page-title An error has occurred
%main{:role => "main"}
%pre= @pre_auth.error_response.body[:error_description]
\ No newline at end of file
%h3.page-title Authorize required
%main{:role => "main"}
%p.h4
Authorize
%strong.text-info= @pre_auth.client.name
to use your account?
- if @pre_auth.scopes
#oauth-permissions
%p This application will be able to:
%ul.text-info
- @pre_auth.scopes.each do |scope|
%li= t scope, scope: [:doorkeeper, :scopes]
%hr/
.actions
= form_tag oauth_authorization_path, method: :post do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
= hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= submit_tag "Authorize", class: "btn btn-success wide pull-left"
= form_tag oauth_authorization_path, method: :delete do
= hidden_field_tag :client_id, @pre_auth.client.uid
= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
= hidden_field_tag :state, @pre_auth.state
= hidden_field_tag :response_type, @pre_auth.response_type
= hidden_field_tag :scope, @pre_auth.scope
= submit_tag "Deny", class: "btn btn-danger prepend-left-10"
\ No newline at end of file
%h3.page-title Authorization code:
%main{:role => "main"}
%code#authorization_code= params[:code]
\ No newline at end of file
- submit_btn_css ||= 'btn btn-link btn-remove'
= form_tag oauth_authorized_application_path(application) do
%input{:name => "_method", :type => "hidden", :value => "delete"}/
= submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-link btn-remove btn-small'
\ No newline at end of file
%header.page-header
%h1 Your authorized applications
%main{:role => "main"}
%table.table.table-striped
%thead
%tr
%th Application
%th Created At
%th
%th
%tbody
- @applications.each do |application|
%tr
%td= application.name
%td= application.created_at.strftime('%Y-%m-%d %H:%M:%S')
%td= render 'delete_form', application: application
\ No newline at end of file
%ul.nav.nav-pills.nav-stacked.nav-stacked-menu %ul.sidebar-subnav
= nav_link(path: 'groups#edit') do = nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group) do = link_to edit_group_path(@group) do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
......
.row .panel.panel-default
.col-md-2
= render 'settings_nav'
.col-md-10
.panel.panel-default
.panel-heading .panel-heading
%strong= @group.name %strong= @group.name
group settings: group settings:
...@@ -39,7 +35,7 @@ ...@@ -39,7 +35,7 @@
.form-actions .form-actions
= f.submit 'Save group', class: "btn btn-save" = f.submit 'Save group', class: "btn btn-save"
.panel.panel-danger .panel.panel-danger
.panel-heading Remove group .panel-heading Remove group
.panel-body .panel-body
%p %p
......
...@@ -9,10 +9,6 @@ ...@@ -9,10 +9,6 @@
To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page. To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
%hr %hr
.row .append-bottom-20
.fixed.sidebar-expand-button.hidden-lg.hidden-md = render 'shared/issuable_filter'
%i.fa.fa-list.fa-2x = render 'shared/issues'
.col-md-3.responsive-side
= render 'shared/filter', entity: 'issue'
.col-md-9
= render 'shared/issues'
.row %h3.page-title Linked LDAP groups
.col-md-2= render 'groups/settings_nav' = render 'ldap_group_links/form', group: @group
.col-md-10 = render 'ldap_group_links/ldap_group_links', group: @group
%h3.page-title Linked LDAP groups
= render 'ldap_group_links/form', group: @group
= render 'ldap_group_links/ldap_group_links', group: @group
...@@ -8,10 +8,6 @@ ...@@ -8,10 +8,6 @@
- if current_user - if current_user
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
%hr %hr
.row .append-bottom-20
.fixed.sidebar-expand-button.hidden-lg.hidden-md = render 'shared/issuable_filter'
%i.fa.fa-list.fa-2x = render 'shared/merge_requests'
.col-md-3.responsive-side
= render 'shared/filter', entity: 'merge_request'
.col-md-9
= render 'shared/merge_requests'
.row .panel.panel-default
.col-md-2
= render 'settings_nav'
.col-md-10
.panel.panel-default
.panel-heading .panel-heading
%strong= @group.name %strong= @group.name
projects: projects:
...@@ -26,4 +22,4 @@ ...@@ -26,4 +22,4 @@
- if @projects.blank? - if @projects.blank?
.nothing-here-block This group has no projects yet .nothing-here-block This group has no projects yet
= paginate @projects, theme: "gitlab" = paginate @projects, theme: "gitlab"
.dashboard .dashboard
%div
= image_tag group_icon(@group.path), class: "avatar s90"
.clearfix
%h2
= @group.name
- if @group.description.present?
%p
= escaped_autolink(@group.description)
%hr
.row
%section.activities.col-md-8.hidden-sm.hidden-xs %section.activities.col-md-8.hidden-sm.hidden-xs
- if current_user - if current_user
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
= link_to dashboard_path, class: 'btn btn-tiny' do
&larr; To dashboard
&nbsp;
%span.cgray
Currently you are only seeing events from the
= @group.name
group
%hr
= render 'shared/event_filter' = render 'shared/event_filter'
- if @events.any? - if @events.any?
.content_list .content_list
...@@ -17,24 +19,6 @@ ...@@ -17,24 +19,6 @@
.nothing-here-block Project activity will be displayed here .nothing-here-block Project activity will be displayed here
= spinner = spinner
%aside.side.col-md-4 %aside.side.col-md-4
.light-well.append-bottom-20
= image_tag group_icon(@group.path), class: "avatar s90"
.clearfix.light
%h3.page-title
= @group.name
- if @group.description.present?
%p
= escaped_autolink(@group.description)
= render "projects", projects: @projects = render "projects", projects: @projects
%br %br
= render "shared_projects", projects: @shared_projects = render "shared_projects", projects: @shared_projects
- if current_user
.prepend-top-20
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed" do
%strong
%i.fa.fa-rss
News Feed
%hr
= render 'shared/promo'
...@@ -2,3 +2,7 @@ ...@@ -2,3 +2,7 @@
.broadcast-message{ style: broadcast_styling(broadcast_message) } .broadcast-message{ style: broadcast_styling(broadcast_message) }
%i.fa.fa-bullhorn %i.fa.fa-bullhorn
= broadcast_message.message = broadcast_message.message
:css
.sidebar-wrapper .nav-sidebar {
margin-top: 58px;
}
...@@ -2,10 +2,8 @@ ...@@ -2,10 +2,8 @@
.navbar-inner .navbar-inner
.container .container
%div.app_logo %div.app_logo
%span.separator
= link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do
%h1 GITLAB %h1 GITLAB
%span.separator
%h1.title= title %h1.title= title
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
......
- if defined?(sidebar)
.page-with-sidebar
.sidebar-wrapper
= render(sidebar)
.content-wrapper
.container-fluid
.content
= render "layouts/flash"
.clearfix
= yield
- else
.container.navless-container
.content
= yield
= yield :embedded_scripts
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Admin area" = render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} admin", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} admin", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Admin area" = render "layouts/head_panel", title: "Admin area"
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/admin'
.container= render 'layouts/nav/admin'
.container
.content
= render "layouts/flash"
= yield
= yield :embedded_scripts
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Dashboard" = render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} application", :'data-page' => body_data_page } %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page }
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard" = render "layouts/head_panel", title: "Dashboard"
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/dashboard'
.container= render 'layouts/nav/dashboard'
.container
.content
= render "layouts/flash"
= yield
!!!
%html
%head
%meta{:charset => "utf-8"}
%meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
%title Doorkeeper
= stylesheet_link_tag "doorkeeper/admin/application"
= csrf_meta_tags
%body
.navbar.navbar-inverse.navbar-fixed-top{:role => "navigation"}
.container
.navbar-header
= link_to 'OAuth2 Provider', oauth_applications_path, class: 'navbar-brand'
%ul.nav.navbar-nav
= content_tag :li, class: "#{'active' if request.path == oauth_applications_path}" do
= link_to 'Applications', oauth_applications_path
.container
- if flash[:notice].present?
.alert.alert-info
= flash[:notice]
= yield
\ No newline at end of file
!!!
%html
%head
%title OAuth authorize required
%meta{:charset => "utf-8"}
%meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
= stylesheet_link_tag "doorkeeper/application"
= csrf_meta_tags
%body
#container
- if flash[:notice].present?
.alert.alert-info
= flash[:notice]
= yield
\ No newline at end of file
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Error" = render "layouts/head", title: "Error"
%body{class: "#{app_theme} application"} %body{class: "#{app_theme} #{theme_type} application"}
= render "layouts/head_panel", title: "" if current_user = render "layouts/head_panel", title: "" if current_user
.container.navless-container .container.navless-container
= render "layouts/flash" = render "layouts/flash"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: page_title = render "layouts/head", title: page_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
- if current_user - if current_user
= render "layouts/head_panel", title: page_title = render "layouts/head_panel", title: page_title
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: group_head_title = render "layouts/head", title: group_head_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "group: #{@group.name}" = render "layouts/head_panel", title: @group.name
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/group'
.container= render 'layouts/nav/group'
.container
.content
= render "layouts/flash"
= yield
%ul %ul.nav.nav-sidebar
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: "Stats" do = link_to admin_root_path, title: "Stats" do
%i.fa.fa-dashboard
%span
Overview Overview
= nav_link(controller: :projects) do = nav_link(controller: :projects) do
= link_to "Projects", admin_projects_path = link_to admin_projects_path do
%i.fa.fa-cube
%span
Projects
= nav_link(controller: :users) do = nav_link(controller: :users) do
= link_to "Users", admin_users_path = link_to admin_users_path do
%i.fa.fa-users
%span
Users
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
= link_to "Groups", admin_groups_path = link_to admin_groups_path do
%i.fa.fa-group
%span
Groups
= nav_link(controller: :logs) do = nav_link(controller: :logs) do
= link_to "Logs", admin_logs_path = link_to admin_logs_path do
%i.fa.fa-file-text
%span
Logs
= nav_link(controller: :broadcast_messages) do = nav_link(controller: :broadcast_messages) do
= link_to "Messages", admin_broadcast_messages_path = link_to admin_broadcast_messages_path do
%i.fa.fa-bullhorn
%span
Messages
= nav_link(controller: :hooks) do = nav_link(controller: :hooks) do
= link_to "Hooks", admin_hooks_path = link_to admin_hooks_path do
%i.fa.fa-external-link
%span
Hooks
= nav_link(controller: :background_jobs) do = nav_link(controller: :background_jobs) do
= link_to "Background Jobs", admin_background_jobs_path = link_to admin_background_jobs_path do
%i.fa.fa-cog
%span
Background Jobs
= nav_link(controller: :appearances) do = nav_link(controller: :appearances) do
= link_to "Appearance", admin_appearances_path = link_to admin_appearances_path do
%i.fa.fa-image
%span
Appearance
%ul %ul.nav.nav-sidebar
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: 'Home', class: 'shortcuts-activity' do = link_to root_path, title: 'Home', class: 'shortcuts-activity' do
%i.fa.fa-dashboard
%span
Activity Activity
= nav_link(path: 'dashboard#projects') do = nav_link(path: 'dashboard#projects') do
= link_to projects_dashboard_path, class: 'shortcuts-projects' do = link_to projects_dashboard_path, class: 'shortcuts-projects' do
%i.fa.fa-cube
%span
Projects Projects
= nav_link(path: 'dashboard#issues') do = nav_link(path: 'dashboard#issues') do
= link_to issues_dashboard_path, class: 'shortcuts-issues' do = link_to issues_dashboard_path, class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues Issues
%span.count= current_user.assigned_issues.opened.count %span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to merge_requests_dashboard_path, class: 'shortcuts-merge_requests' do = link_to merge_requests_dashboard_path, class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count %span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do = nav_link(controller: :help) do
= link_to "Help", help_path = link_to help_path do
%i.fa.fa-question-circle
%span
Help
%ul %ul.nav.nav-sidebar
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: "Home" do = link_to group_path(@group), title: "Home" do
%i.fa.fa-dashboard
%span
Activity Activity
- if current_user
= nav_link(controller: [:group, :milestones]) do = nav_link(controller: [:group, :milestones]) do
= link_to group_milestones_path(@group) do = link_to group_milestones_path(@group) do
%i.fa.fa-clock-o
%span
Milestones Milestones
= nav_link(path: 'groups#issues') do = nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group) do = link_to issues_group_path(@group) do
%i.fa.fa-exclamation-circle
%span
Issues Issues
- if current_user - if current_user
%span.count= current_user.assigned_issues.opened.of_group(@group).count %span.count= Issue.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do = nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group) do = link_to merge_requests_group_path(@group) do
%i.fa.fa-tasks
%span
Merge Requests Merge Requests
- if current_user - if current_user
%span.count= current_user.cared_merge_requests.opened.of_group(@group).count %span.count= MergeRequest.opened.of_group(@group).count
= nav_link(path: 'groups#members') do = nav_link(path: 'groups#members') do
= link_to "Members", members_group_path(@group) = link_to members_group_path(@group) do
%i.fa.fa-users
%span
Members
- if can?(current_user, :manage_group, @group) - if can?(current_user, :manage_group, @group)
= nav_link(path: 'groups#edit') do = nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
= link_to edit_group_path(@group), class: "tab " do = link_to edit_group_path(@group), class: "tab no-highlight" do
%i.fa.fa-cogs
%span
Settings Settings
%i.fa.fa-angle-down
- if group_settings_page?
= render 'groups/settings_nav'
%ul %ul.nav.nav-sidebar
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: "Profile" do = link_to profile_path, title: "Profile" do
%i.fa.fa-user
%span
Profile Profile
= nav_link(controller: :accounts) do = nav_link(controller: :accounts) do
= link_to "Account", profile_account_path = link_to profile_account_path do
%i.fa.fa-gear
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
= link_to applications_profile_path do
%i.fa.fa-cloud
%span
Applications
= nav_link(controller: :emails) do = nav_link(controller: :emails) do
= link_to profile_emails_path do = link_to profile_emails_path do
%i.fa.fa-envelope-o
%span
Emails Emails
%span.count= current_user.emails.count + 1 %span.count= current_user.emails.count + 1
- unless current_user.ldap_user? - unless current_user.ldap_user?
= nav_link(controller: :passwords) do = nav_link(controller: :passwords) do
= link_to "Password", edit_profile_password_path = link_to edit_profile_password_path do
%i.fa.fa-lock
%span
Password
= nav_link(controller: :notifications) do = nav_link(controller: :notifications) do
= link_to "Notifications", profile_notifications_path = link_to profile_notifications_path do
%i.fa.fa-inbox
%span
Notifications
= nav_link(controller: :keys) do = nav_link(controller: :keys) do
= link_to profile_keys_path do = link_to profile_keys_path do
%i.fa.fa-key
%span
SSH Keys SSH Keys
%span.count= current_user.keys.count %span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do = nav_link(path: 'profiles#design') do
= link_to "Design", design_profile_path = link_to design_profile_path do
%i.fa.fa-image
%span
Design
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
= link_to "Groups", profile_groups_path = link_to profile_groups_path do
%i.fa.fa-group
%span
Groups
= nav_link(path: 'profiles#history') do = nav_link(path: 'profiles#history') do
= link_to "History", history_profile_path = link_to history_profile_path do
%i.fa.fa-history
%span
History
%ul.project-navigation %ul.project-navigation.nav.nav-sidebar
= nav_link(path: 'projects#show', html_options: {class: "home"}) do = nav_link(path: 'projects#show', html_options: {class: "home"}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
%i.fa.fa-dashboard
%span
Project Project
- if project_nav_tab? :files - if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' = link_to project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' do
%i.fa.fa-files-o
%span
Files
- if project_nav_tab? :commits - if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do = nav_link(controller: %w(commit commits compare repositories tags branches)) do
= link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' = link_to project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' do
%i.fa.fa-history
%span
Commits
- if project_nav_tab? :network - if project_nav_tab? :network
= nav_link(controller: %w(network)) do = nav_link(controller: %w(network)) do
= link_to "Network", project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' = link_to project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' do
%i.fa.fa-code-fork
%span
Network
- if project_nav_tab? :graphs - if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do = nav_link(controller: %w(graphs)) do
= link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' = link_to project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' do
%i.fa.fa-area-chart
%span
Graphs
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do = nav_link(controller: %w(issues milestones labels)) do
= link_to url_for_project_issues, class: 'shortcuts-issues' do = link_to url_for_project_issues, class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues Issues
- if @project.used_default_issues_tracker? - if @project.used_default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count %span.count.issue_counter= @project.issues.opened.count
...@@ -28,18 +45,32 @@ ...@@ -28,18 +45,32 @@
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
= link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count %span.count.merge_counter= @project.merge_requests.opened.count
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do = nav_link(controller: :wikis) do
= link_to 'Wiki', project_wiki_path(@project, :home), class: 'shortcuts-wiki' = link_to project_wiki_path(@project, :home), class: 'shortcuts-wiki' do
%i.fa.fa-book
%span
Wiki
- if project_nav_tab? :snippets - if project_nav_tab? :snippets
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to 'Snippets', project_snippets_path(@project), class: 'shortcuts-snippets' = link_to project_snippets_path(@project), class: 'shortcuts-snippets' do
%i.fa.fa-file-text-o
%span
Snippets
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class}"}) do = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), class: "stat-tab tab " do = link_to edit_project_path(@project), class: "stat-tab tab no-highlight" do
%i.fa.fa-cogs
%span
Settings Settings
%i.fa.fa-angle-down
- if @project_settings_nav
= render 'projects/settings_nav'
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: @title = render "layouts/head", title: @title
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: @title = render "layouts/head_panel", title: @title
.container.navless-container .container.navless-container
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Profile" = render "layouts/head", title: "Profile"
%body{class: "#{app_theme} profile", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} profile", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Profile" = render "layouts/head_panel", title: "Profile"
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/profile'
.container= render 'layouts/nav/profile'
.container
.content
= render "layouts/flash"
= yield
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace = render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } %body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project) = render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete" = render "layouts/init_auto_complete"
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
= render 'shared/no_ssh' = render 'shared/no_ssh'
%nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/project' - @project_settings_nav = true
.container = render 'layouts/page', sidebar: 'layouts/nav/project'
.content
= render "layouts/flash"
.row
.col-md-2
= render "projects/settings_nav"
.col-md-10
= yield
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: project_head_title = render "layouts/head", title: project_head_title
%body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } %body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project) = render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete" = render "layouts/init_auto_complete"
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
= render 'shared/no_ssh' = render 'shared/no_ssh'
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/project'
.container= render 'layouts/nav/project'
.container
.content
= render "layouts/flash"
= yield
= yield :embedded_scripts
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: group_head_title = render "layouts/head", title: group_head_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/public_head_panel", title: "group: #{@group.name}" = render "layouts/public_head_panel", title: "group: #{@group.name}"
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/group'
.container= render 'layouts/nav/group'
.container
.content= yield
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace = render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project) = render "layouts/public_head_panel", title: project_title(@project)
%nav.main-nav.navbar-collapse.collapse = render 'layouts/page', sidebar: 'layouts/nav/project'
.container= render 'layouts/nav/project'
.container
.content= yield
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: @title = render "layouts/head", title: @title
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/public_head_panel", title: @title = render "layouts/public_head_panel", title: @title
.container.navless-container = render 'layouts/page'
.content= yield
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Search" = render "layouts/head", title: "Search"
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Search" = render "layouts/head_panel", title: "Search"
.container.navless-container .container.navless-container
......
...@@ -75,3 +75,4 @@ ...@@ -75,3 +75,4 @@
The following groups will be abandoned. You should transfer or remove them: The following groups will be abandoned. You should transfer or remove them:
%strong #{current_user.solo_owned_groups.map(&:name).join(', ')} %strong #{current_user.solo_owned_groups.map(&:name).join(', ')}
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
%h3.page-title
OAuth2
%fieldset.oauth-applications
%legend Your applications
%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
- if @applications.any?
%table.table.table-striped
%thead
%tr
%th Name
%th Callback URL
%th Clients
%th
%th
%tbody
- @applications.each do |application|
%tr{:id => "application_#{application.id}"}
%td= link_to application.name, oauth_application_path(application)
%td
- application.redirect_uri.split.each do |uri|
%div= uri
%td= application.access_tokens.count
%td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-small'
%td= render 'doorkeeper/applications/delete_form', application: application
%fieldset.oauth-authorized-applications.prepend-top-20
%legend Authorized applications
- if @authorized_tokens.any?
%table.table.table-striped
%thead
%tr
%th Name
%th Authorized At
%th Scope
%th
%tbody
- @authorized_tokens.each do |token|
- application = token.application
%tr{:id => "application_#{application.id}"}
%td= application.name
%td= token.created_at
%td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', application: application
- else
%p.light You dont have any authorized applications
// Remove body class for any previous theme, re-add current one // Remove body class for any previous theme, re-add current one
$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color') $('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color light_theme dark_theme')
$('body').addClass('<%= app_theme %>') $('body').addClass('<%= app_theme %> <%= theme_type %>')
// Re-render the header to reflect the new theme // Re-render the header to reflect the new theme
$('header').html('<%= escape_javascript(render("layouts/head_panel", title: "Profile")) %>') $('header').html('<%= escape_javascript(render("layouts/head_panel", title: "Profile")) %>')
......
...@@ -2,15 +2,22 @@ ...@@ -2,15 +2,22 @@
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: :issues) do = nav_link(controller: :issues) do
= link_to project_issues_path(@project), class: "tab" do = link_to project_issues_path(@project), class: "tab" do
%i.fa.fa-exclamation-circle
Issues Issues
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: :merge_requests) do
= link_to project_merge_requests_path(@project), class: "tab" do = link_to project_merge_requests_path(@project), class: "tab" do
%i.fa.fa-tasks
Merge Requests Merge Requests
= nav_link(controller: :milestones) do = nav_link(controller: :milestones) do
= link_to 'Milestones', project_milestones_path(@project), class: "tab" = link_to project_milestones_path(@project), class: "tab" do
%i.fa.fa-clock-o
Milestones
= nav_link(controller: :labels) do = nav_link(controller: :labels) do
= link_to 'Labels', project_labels_path(@project), class: "tab" = link_to project_labels_path(@project), class: "tab" do
%i.fa.fa-tags
Labels
- if current_controller?(:milestones) - if current_controller?(:milestones)
%li.pull-right %li.pull-right
......
%ul.nav.nav-pills.nav-stacked.nav-stacked-menu.append-bottom-20.project-settings-nav %ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do = nav_link(path: 'projects#edit') do
= link_to edit_project_path(@project), class: "stat-tab tab " do = link_to edit_project_path(@project), class: "stat-tab tab " do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
%span
Project Project
= nav_link(controller: [:team_members, :teams]) do = nav_link(controller: [:team_members, :teams]) do
= link_to project_team_index_path(@project), class: "team-tab tab" do = link_to project_team_index_path(@project), class: "team-tab tab" do
%i.fa.fa-users %i.fa.fa-users
%span
Members Members
= nav_link(controller: :group_links) do = nav_link(controller: :group_links) do
= link_to project_group_links_path(@project) do = link_to project_group_links_path(@project) do
%i.fa.fa-share-square-o %i.fa.fa-share-square-o
%span
Groups Groups
= nav_link(controller: :deploy_keys) do = nav_link(controller: :deploy_keys) do
= link_to project_deploy_keys_path(@project) do = link_to project_deploy_keys_path(@project) do
%i.fa.fa-key %i.fa.fa-key
%span
Deploy Keys Deploy Keys
= nav_link(controller: :hooks) do = nav_link(controller: :hooks) do
= link_to project_hooks_path(@project) do = link_to project_hooks_path(@project) do
%i.fa.fa-link %i.fa.fa-link
%span
Web Hooks Web Hooks
= nav_link(controller: :git_hooks) do = nav_link(controller: :git_hooks) do
= link_to project_git_hooks_path(@project) do = link_to project_git_hooks_path(@project) do
%i.fa.fa-git-square %i.fa.fa-git-square
%span
Git Hooks Git Hooks
= nav_link(controller: :services) do = nav_link(controller: :services) do
= link_to project_services_path(@project) do = link_to project_services_path(@project) do
%i.fa.fa-cogs %i.fa.fa-cogs
%span
Services Services
= nav_link(controller: :protected_branches) do = nav_link(controller: :protected_branches) do
= link_to project_protected_branches_path(@project) do = link_to project_protected_branches_path(@project) do
%i.fa.fa-lock %i.fa.fa-lock
%span
Protected branches Protected branches
= nav_link(controller: :audit_events) do = nav_link(controller: :audit_events) do
= link_to project_audit_events_path(@project) do = link_to project_audit_events_path(@project) do
%i.fa.fa-file-text-o %i.fa.fa-file-text-o
%span
Audit Events Audit Events
.btn-group.tree-btn-group .btn-group.tree-btn-group
-# only show edit link for text files = edit_blob_link(@project, @ref, @path)
- if @blob.text?
- if allowed_tree_edit?
= link_to 'Edit', project_edit_tree_path(@project, @id),
class: 'btn btn-small'
- else
%span.btn.btn-small.disabled Edit
= link_to 'Raw', project_raw_path(@project, @id), = link_to 'Raw', project_raw_path(@project, @id),
class: 'btn btn-small', target: '_blank' class: 'btn btn-small', target: '_blank'
-# only show normal/blame view links for text files -# only show normal/blame view links for text files
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= link_to title, '#' = link_to title, '#'
%ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs %ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs
- blob_commit = @repository.last_commit_for_path(@commit.id, @blob.path) - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project = render blob_commit, project: @project
%div#tree-content-holder.tree-content-holder %div#tree-content-holder.tree-content-holder
......
...@@ -2,15 +2,9 @@ ...@@ -2,15 +2,9 @@
.col-md-8 .col-md-8
= render 'projects/diffs/stats', diffs: diffs = render 'projects/diffs/stats', diffs: diffs
.col-md-4 .col-md-4
%ul.nav.nav-tabs .btn-group.pull-right
%li.pull-right{class: params[:view] == 'parallel' ? 'active' : ''} = inline_diff_btn
- params_copy = params.dup = parallel_diff_btn
- params_copy[:view] = 'parallel'
= link_to "Side-by-side Diff", url_for(params_copy), {id: "commit-diff-viewtype"}
%li.pull-right{class: params[:view] != 'parallel' ? 'active' : ''}
- params_copy[:view] = 'inline'
= link_to "Inline Diff", url_for(params_copy), {id: "commit-diff-viewtype"}
- if show_diff_size_warning?(diffs) - if show_diff_size_warning?(diffs)
= render 'projects/diffs/warning', diffs: diffs = render 'projects/diffs/warning', diffs: diffs
......
...@@ -30,9 +30,9 @@ ...@@ -30,9 +30,9 @@
&nbsp; &nbsp;
- if @merge_request && @merge_request.source_project - if @merge_request && @merge_request.source_project
= link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff_file.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do = edit_blob_link(@merge_request.source_project,
Edit @merge_request.source_branch, diff_file.new_path,
&nbsp; after: '&nbsp;', from_merge_request_id: @merge_request.id)
= view_file_btn(@commit.id, diff_file, project) = view_file_btn(@commit.id, diff_file, project)
......
...@@ -150,6 +150,8 @@ ...@@ -150,6 +150,8 @@
.col-sm-9 .col-sm-9
.form-group .form-group
.input-group .input-group
.input-group-addon
#{URI.join(root_url, @project.namespace.path)}/
= f.text_field :path, class: 'form-control' = f.text_field :path, class: 'form-control'
%span.input-group-addon .git %span.input-group-addon .git
%ul %ul
......
- content_for :note_actions do
- if can?(current_user, :modify_issue, @issue)
- if @issue.closed?
= link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue'
- else
= link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue"
.row
.col-md-9
.participants
%cite.cgray
= pluralize(@issue.participants.count, 'participant')
- @issue.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
.voting_notes#notes= render "projects/notes/notes_with_form"
.col-md-3.hidden-sm.hidden-xs
%div
.clearfix
%span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @issue)
%hr
.clearfix
.votes-holder
%h6 Votes
#votes= render 'votes/votes_block', votable: @issue
%hr
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
- if @issue.labels.any?
%hr
%h6 Labels
.issue-show-labels
- @issue.labels.each do |label|
= link_to project_issues_path(@project, label_name: label.name) do
%p= render_colored_label(label)
= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| = form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
.row %div.prepend-top-20
.col-sm-6 %p
%strong.append-right-10
Assignee: Assignee:
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
...@@ -11,8 +10,8 @@ ...@@ -11,8 +10,8 @@
- else - else
None None
.col-sm-6.text-right %div.prepend-top-20
%strong.append-right-10 %p
Milestone: Milestone:
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
......
.append-bottom-10 .append-bottom-10
.check-all-holder .check-all-holder
= check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left"
= render 'projects/issuable_filter' = render 'shared/issuable_filter'
.clearfix .clearfix
.issues_bulk_update.hide .issues_bulk_update.hide
......
= render "projects/issues_nav" = render "projects/issues_nav"
.row
.fixed.fixed.sidebar-expand-button.hidden-lg.hidden-md.hidden-xs .issues-holder
%i.fa.fa-list.fa-2x
.col-md-3.responsive-side
= render 'shared/project_filter', project_entities_path: project_issues_path(@project),
labels: true, redirect: 'issues', entity: 'issue'
.col-md-9.issues-holder
= render "issues" = render "issues"
%h3.page-title %h4.page-title
.issue-box{ class: issue_box_class(@issue) }
- if @issue.closed?
Closed
- else
Open
Issue ##{@issue.iid} Issue ##{@issue.iid}
%small.creator
&middot; created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)}
%span.pull-right.issue-btn-group .pull-right
- if can?(current_user, :write_issue, @project) - if can?(current_user, :write_issue, @project)
= link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do = link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus %i.fa.fa-plus
...@@ -12,68 +19,19 @@ ...@@ -12,68 +19,19 @@
- else - else
= link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue" = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue"
= link_to edit_project_issue_path(@project, @issue), class: "btn btn-grouped" do = link_to edit_project_issue_path(@project, @issue), class: "btn btn-grouped issuable-edit" do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
Edit Edit
.clearfix %hr
.votes-holder %h3.issue-title
#votes= render 'votes/votes_block', votable: @issue
.back-link
= link_to project_issues_path(@project) do
&larr; To issues list
%span.milestone-nav-link
- if @issue.milestone
|
%span.light Milestone
= link_to project_milestone_path(@project, @issue.milestone) do
= @issue.milestone.title
.issue-box{ class: issue_box_class(@issue) }
.state.clearfix
.state-label
- if @issue.closed?
Closed
- else
Open
.cross-project-ref
%i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @issue)
.creator
Created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)}
%h4.title
= gfm escape_once(@issue.title) = gfm escape_once(@issue.title)
%div
- if @issue.description.present? - if @issue.description.present?
.description .description
.wiki .wiki
= preserve do = preserve do
= markdown(@issue.description, parse_tasks: true) = markdown(@issue.description, parse_tasks: true)
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
- content_for :note_actions do
- if can?(current_user, :modify_issue, @issue)
- if @issue.closed?
= link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue'
- else
= link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue"
.participants
%cite.cgray
= pluralize(@issue.participants.count, 'participant')
- @issue.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
.issue-show-labels.pull-right
- @issue.labels.each do |label|
= link_to project_issues_path(@project, label_name: label.name) do
= render_colored_label(label)
.voting_notes#notes= render "projects/notes/notes_with_form" %hr
= render "projects/issues/discussion"
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
:plain :plain
$("##{dom_id(@issue)}").fadeOut(); $("##{dom_id(@issue)}").fadeOut();
- elsif params[:issue_context] - elsif params[:issue_context]
$('.issue-box .context').effect('highlight'); $('.context').effect('highlight');
- if @issue.milestone - if @issue.milestone
$('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, project_milestone_path(@issue.project, @issue.milestone))}</span>") $('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, project_milestone_path(@issue.project, @issue.milestone))}</span>")
- else - else
......
- content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open?
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request"
- if @merge_request.closed?
= link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
.row
.col-md-9
= render "projects/merge_requests/show/participants"
= render "projects/notes/notes_with_form"
.col-md-3.hidden-sm.hidden-xs
.clearfix
%span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @merge_request)
%hr
.votes-holder.hidden-sm.hidden-xs
%h6 Votes
#votes= render 'votes/votes_block', votable: @merge_request
%hr
.context
%cite.cgray
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
- if @merge_request.labels.any?
%hr
%h6 Labels
.merge-request-show-labels
- @merge_request.labels.each do |label|
= link_to project_merge_requests_path(@project, label_name: label.name) do
%p= render_colored_label(label)
.merge-request .merge-request
= render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_title"
= render "projects/merge_requests/show/how_to_merge" %hr
= render "projects/merge_requests/show/mr_box" = render "projects/merge_requests/show/mr_box"
%hr
.append-bottom-20
.slead
%span From
- if @merge_request.for_fork?
%strong.label-branch<
- if @merge_request.source_project
= link_to @merge_request.source_project_namespace, project_path(@merge_request.source_project)
- else
\ #{@merge_request.source_project_namespace}
\:#{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
- else
%strong.label-branch #{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_branch}
- if @merge_request.open?
%span.pull-right
.btn-group
%a.btn.dropdown-toggle{ data: {toggle: :dropdown} }
%i.fa.fa-download
Download as
%span.caret
%ul.dropdown-menu
%li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
%li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff)
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/show/state_widget" = render "projects/merge_requests/show/state_widget"
= render "projects/merge_requests/show/commits"
= render "projects/merge_requests/show/participants"
- if @commits.present? - if @commits.present?
%ul.nav.nav-pills.merge-request-tabs %ul.nav.nav-tabs.merge-request-tabs
%li.notes-tab{data: {action: 'notes'}} %li.notes-tab{data: {action: 'notes'}}
= link_to project_merge_request_path(@project, @merge_request) do = link_to project_merge_request_path(@project, @merge_request) do
%i.fa.fa-comment %i.fa.fa-comments
Discussion Discussion
%span.badge= @merge_request.mr_and_commit_notes.count %span.badge= @merge_request.mr_and_commit_notes.count
%li.commits-tab{data: {action: 'commits'}}
= link_to project_merge_request_path(@project, @merge_request), title: 'Commits' do
%i.fa.fa-database
Commits
%span.badge= @commits.size
%li.diffs-tab{data: {action: 'diffs'}} %li.diffs-tab{data: {action: 'diffs'}}
= link_to diffs_project_merge_request_path(@project, @merge_request) do = link_to diffs_project_merge_request_path(@project, @merge_request) do
%i.fa.fa-list-alt %i.fa.fa-list-alt
Changes Changes
%span.badge= @merge_request.diffs.size %span.badge= @merge_request.diffs.size
- content_for :note_actions do .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
- if can?(current_user, :modify_merge_request, @merge_request) = render "projects/merge_requests/discussion"
- if @merge_request.open? .commits.tab-content
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" = render "projects/merge_requests/show/commits"
- if @merge_request.closed?
= link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
.diffs.tab-content .diffs.tab-content
- if current_page?(action: 'diffs') - if current_page?(action: 'diffs')
= render "projects/merge_requests/show/diffs" = render "projects/merge_requests/show/diffs"
.notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render "projects/notes/notes_with_form"
.mr-loading-status .mr-loading-status
= spinner = spinner
:javascript :javascript
var merge_request; var merge_request;
......
= render "projects/issues_nav" = render "projects/issues_nav"
.row .merge-requests-holder
.col-md-3.responsive-side
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project),
labels: true, redirect: 'merge_requests', entity: 'merge_request'
.col-md-9
.append-bottom-10 .append-bottom-10
= render 'projects/issuable_filter' = render 'shared/issuable_filter'
.panel.panel-default .panel.panel-default
%ul.well-list.mr-list %ul.well-list.mr-list
= render @merge_requests = render @merge_requests
......
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| = form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
.row %div.prepend-top-20
.col-sm-6 %p
%strong.append-right-10
Assignee: Assignee:
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
...@@ -11,8 +10,8 @@ ...@@ -11,8 +10,8 @@
- else - else
None None
.col-sm-6.text-right %div.prepend-top-20
%strong.append-right-10 %p
Milestone: Milestone:
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
......
.issue-box{ class: issue_box_class(@merge_request) } %h3.issue-title
.state.clearfix
.state-label
- if @merge_request.merged?
Merged
- elsif @merge_request.closed?
Closed
- else
Open
.cross-project-ref
%i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'}
= cross_project_reference(@project, @merge_request)
.creator
Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
%h4.title
= gfm escape_once(@merge_request.title) = gfm escape_once(@merge_request.title)
%div
- if @merge_request.description.present? - if @merge_request.description.present?
.description .description
.wiki .wiki
= preserve do = preserve do
= markdown(@merge_request.description, parse_tasks: true) = markdown(@merge_request.description, parse_tasks: true)
.context
%cite.cgray
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
%h3.page-title %h4.page-title
.issue-box{ class: issue_box_class(@merge_request) }
- if @merge_request.merged?
Merged
- elsif @merge_request.closed?
Closed
- else
Open
= "Merge Request ##{@merge_request.iid}" = "Merge Request ##{@merge_request.iid}"
%small.creator
&middot;
created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
%span.pull-right.issue-btn-group .issue-btn-group.pull-right
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open? - if @merge_request.open?
.btn-group.pull-left
%a.btn.btn-grouped.dropdown-toggle{ data: {toggle: :dropdown} }
%i.fa.fa-download
Download as
%span.caret
%ul.dropdown-menu
%li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
%li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff)
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request" = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request"
= link_to edit_project_merge_request_path(@project, @merge_request), class: "btn btn-grouped issuable-edit", id: "edit_merge_request" do
= link_to edit_project_merge_request_path(@project, @merge_request), class: "btn btn-grouped", id:"edit_merge_request" do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
Edit Edit
- if @merge_request.closed? - if @merge_request.closed?
= link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request" = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request"
.votes-holder.hidden-sm.hidden-xs
#votes= render 'votes/votes_block', votable: @merge_request
.back-link
= link_to project_merge_requests_path(@project) do
&larr; To merge requests
%span.prepend-left-20
%span From
- if @merge_request.for_fork?
%strong.label-branch<
- if @merge_request.source_project
= link_to @merge_request.source_project_namespace, project_path(@merge_request.source_project)
- else
\ #{@merge_request.source_project_namespace}
\:#{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
- else
%strong.label-branch #{@merge_request.source_branch}
%span into
%strong.label-branch #{@merge_request.target_branch}
...@@ -2,8 +2,3 @@ ...@@ -2,8 +2,3 @@
%cite.cgray #{@merge_request.participants.count} participants %cite.cgray #{@merge_request.participants.count} participants
- @merge_request.participants.each do |participant| - @merge_request.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24) = link_to_member(@project, participant, name: false, size: 24)
.merge-request-show-labels.pull-right
- @merge_request.labels.each do |label|
= link_to project_merge_requests_path(@project, label_name: label.name) do
= render_colored_label(label)
- if params[:merge_request_context] - if params[:merge_request_context]
$('.issue-box .context').effect('highlight'); $('.context').effect('highlight');
= render "projects/issues_nav" = render "projects/issues_nav"
%h3.page-title %h3.page-title
Milestone ##{@milestone.iid} .issue-box{ class: issue_box_class(@milestone) }
.pull-right
- if can?(current_user, :admin_milestone, @project)
= link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
- if @milestone.active?
= link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
- else
= link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
- if @milestone.issues.any? && @milestone.can_be_closed?
.alert.alert-success
%span All issues for this milestone are closed. You may close milestone now.
.back-link
= link_to project_milestones_path(@project) do
&larr; To milestones list
.issue-box{ class: issue_box_class(@milestone) }
.state.clearfix
.state-label
- if @milestone.closed? - if @milestone.closed?
Closed Closed
- elsif @milestone.expired? - elsif @milestone.expired?
Expired Expired
- else - else
Open Open
.creator Milestone ##{@milestone.iid}
= @milestone.expires_at .pull-right.creator
%small= @milestone.expires_at
%h4.title %hr
- if @milestone.issues.any? && @milestone.can_be_closed?
.alert.alert-success
%span All issues for this milestone are closed. You may close milestone now.
.row
.col-sm-9
%h3.issue-title
= gfm escape_once(@milestone.title) = gfm escape_once(@milestone.title)
%div
- if @milestone.description.present? - if @milestone.description.present?
.description .description
.wiki .wiki
= preserve do = preserve do
= markdown @milestone.description = markdown @milestone.description
%hr
.context .context
%p %p.lead
Progress: Progress:
#{@milestone.closed_items_count} closed #{@milestone.closed_items_count} closed
&ndash; &ndash;
...@@ -53,6 +39,22 @@ ...@@ -53,6 +39,22 @@
.progress.progress-info .progress.progress-info
.progress-bar{style: "width: #{@milestone.percent_complete}%;"} .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
.col-sm-3
%div
- if can?(current_user, :admin_milestone, @project)
= link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-block" do
%i.fa.fa-pencil-square-o
Edit
- if @milestone.active?
= link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-block"
- else
= link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-block"
= link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-block", title: "New Issue" do
%i.fa.fa-plus
New Issue
= link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-block"
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li.active %li.active
...@@ -69,10 +71,6 @@ ...@@ -69,10 +71,6 @@
%span.badge= @users.count %span.badge= @users.count
.pull-right .pull-right
= link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small btn-grouped", title: "New Issue" do
%i.fa.fa-plus
New Issue
= link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn btn-small edit-milestone-link btn-grouped"
.tab-content .tab-content
.tab-pane.active#tab-issues .tab-pane.active#tab-issues
......
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
= form_for @project, html: { class: 'new_project form-horizontal' } do |f| = form_for @project, html: { class: 'new_project form-horizontal' } do |f|
.form-group.project-name-holder .form-group.project-name-holder
= f.label :name, class: 'control-label' do = f.label :path, class: 'control-label' do
%strong Project name %strong Project path
.col-sm-10 .col-sm-10
= f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true .input-group
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true
.input-group-addon
\.git
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
.form-group .form-group
...@@ -18,22 +21,6 @@ ...@@ -18,22 +21,6 @@
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2}
%hr %hr
.js-toggle-container
.form-group
.col-sm-2
.col-sm-10
= link_to "#", class: 'js-toggle-button' do
%i.fa.fa-pencil-square-o
%span Customize repository name?
.js-toggle-content.hide
.form-group
= f.label :path, class: 'control-label' do
%span Repository name
.col-sm-10
.input-group
= f.text_field :path, class: 'form-control'
%span.input-group-addon .git
.js-toggle-container .js-toggle-container
.form-group .form-group
.col-sm-2 .col-sm-2
......
%li.timeline-entry{ id: dom_id(note), class: dom_class(note), data: { discussion: note.discussion_id } } %li.timeline-entry{ id: dom_id(note), class: [dom_class(note), ('system-note' if note.system)], data: { discussion: note.discussion_id } }
.timeline-entry-inner .timeline-entry-inner
.timeline-icon .timeline-icon
- if note.system
%span.fa.fa-circle
- else
= image_tag avatar_icon(note.author_email), class: "avatar s40" = image_tag avatar_icon(note.author_email), class: "avatar s40"
.timeline-content .timeline-content
.note-header .note-header
...@@ -17,6 +20,8 @@ ...@@ -17,6 +20,8 @@
= link_to project_note_path(@project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do
%i.fa.fa-trash-o.cred %i.fa.fa-trash-o.cred
Remove Remove
- if note.system
= image_tag avatar_icon(note.author_email), class: "avatar s16"
= link_to_member(@project, note.author, avatar: false) = link_to_member(@project, note.author, avatar: false)
%span.author-username %span.author-username
= '@' + note.author.username = '@' + note.author.username
...@@ -41,7 +46,7 @@ ...@@ -41,7 +46,7 @@
.note-edit-form .note-edit-form
= form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f| = form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f|
= render layout: 'projects/md_preview' do = render layout: 'projects/md_preview' do
= f.text_area :note, class: 'note_text js-note-text markdown-area js-gfm-input turn-on' = f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on'
.form-actions.clearfix .form-actions.clearfix
= f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button" = f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button"
......
- if @project.errors[:namespace_id].present? :plain
:plain
$("#tab-transfer .errors-holder").replaceWith(errorMessage('#{escape_javascript(@project.errors[:namespace_id].first)}'));
$("#tab-transfer .form-actions input").removeAttr('disabled').removeClass('disabled');
- else
:plain
location.href = "#{edit_project_path(@project)}"; location.href = "#{edit_project_path(@project)}";
.event_filter %ul.nav.nav-pills.event_filter
= event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team' = event_filter_link EventFilter.team, 'Team'
- if current_user
- if current_controller?(:dashboard)
%li.pull-right
= link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
%i.fa.fa-rss
News Feed
- if current_controller?(:groups)
%li.pull-right
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
%i.fa.fa-rss
News Feed
%hr
.side-filters
= form_tag filter_path(entity), method: 'get' do
- if current_user
%fieldset.scope-filter
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if params[:scope] == 'assigned-to-me')}
= link_to filter_path(entity, scope: 'assigned-to-me') do
Assigned to me
%span.pull-right
= assigned_entities_count(current_user, entity, @group)
%li{class: ("active" if params[:scope] == 'authored')}
= link_to filter_path(entity, scope: 'authored') do
Created by me
%span.pull-right
= authored_entities_count(current_user, entity, @group)
%li{class: ("active" if params[:scope] == 'all')}
= link_to filter_path(entity, scope: 'all') do
Everyone's
%span.pull-right
= authorized_entities_count(current_user, entity, @group)
%fieldset.status-filter
%legend State
%ul.nav.nav-pills
%li{class: ("active" if params[:state] == 'opened')}
= link_to filter_path(entity, state: 'opened') do
Open
%li{class: ("active" if params[:state] == 'closed')}
= link_to filter_path(entity, state: 'closed') do
Closed
%li{class: ("active" if params[:state] == 'all')}
= link_to filter_path(entity, state: 'all') do
All
%fieldset
%legend Projects
%ul.nav.nav-pills.nav-stacked.nav-small
- @projects.each do |project|
- unless entities_per_project(project, entity).zero?
%li{class: ("active" if params[:project_id] == project.id.to_s)}
= link_to filter_path(entity, project_id: project.id) do
= project.name_with_namespace
%small.pull-right= entities_per_project(project, entity)
%fieldset
- if params[:state].present? || params[:project_id].present?
= link_to filter_path(entity, state: nil, project_id: nil), class: 'pull-right cgray' do
%i.fa.fa-times
%strong Clear filter
.form-group - if @group.persisted?
.form-group
= f.label :name, class: 'control-label' do = f.label :name, class: 'control-label' do
Group name Group name
.col-sm-10 .col-sm-10
= f.text_field :name, placeholder: 'Example Group', class: 'form-control', = f.text_field :name, placeholder: 'open-source', class: 'form-control'
.form-group
= f.label :path, class: 'control-label' do
Group path
.col-sm-10
.input-group
.input-group-addon
= root_url
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
autofocus: local_assigns[:autofocus] || false autofocus: local_assigns[:autofocus] || false
- if @group.persisted?
.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-group.group-description-holder .form-group.group-description-holder
= f.label :description, 'Details', class: 'control-label' = f.label :description, 'Details', class: 'control-label'
......
.issues-filters .issues-filters
.dropdown.inline .pull-left.append-right-20
%ul.nav.nav-pills.nav-compact
%li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened') do
%i.fa.fa-exclamation-circle
Open
%li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed') do
%i.fa.fa-check-circle
Closed
%li{class: ("active" if params[:state] == 'all')}
= link_to page_filter_path(state: 'all') do
%i.fa.fa-compass
All
.dropdown.inline.assignee-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-user %i.fa.fa-user
%span.light assignee: %span.light assignee:
...@@ -12,17 +27,17 @@ ...@@ -12,17 +27,17 @@
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu
%li %li
= link_to project_filter_path(assignee_id: nil) do = link_to page_filter_path(assignee_id: nil) do
Any Any
= link_to project_filter_path(assignee_id: 0) do = link_to page_filter_path(assignee_id: 0) do
Unassigned Unassigned
- @assignees.sort_by(&:name).each do |user| - @assignees.sort_by(&:name).each do |user|
%li %li
= link_to project_filter_path(assignee_id: user.id) do = link_to page_filter_path(assignee_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name = user.name
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10.author-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-user %i.fa.fa-user
%span.light author: %span.light author:
...@@ -35,17 +50,17 @@ ...@@ -35,17 +50,17 @@
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu
%li %li
= link_to project_filter_path(author_id: nil) do = link_to page_filter_path(author_id: nil) do
Any Any
= link_to project_filter_path(author_id: 0) do = link_to page_filter_path(author_id: 0) do
Unassigned Unassigned
- @authors.sort_by(&:name).each do |user| - @authors.sort_by(&:name).each do |user|
%li %li
= link_to project_filter_path(author_id: user.id) do = link_to page_filter_path(author_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name = user.name
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-clock-o %i.fa.fa-clock-o
%span.light milestone: %span.light milestone:
...@@ -58,15 +73,40 @@ ...@@ -58,15 +73,40 @@
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu
%li %li
= link_to project_filter_path(milestone_id: nil) do = link_to page_filter_path(milestone_id: nil) do
Any Any
= link_to project_filter_path(milestone_id: 0) do = link_to page_filter_path(milestone_id: 0) do
None (backlog) None (backlog)
- project_active_milestones.each do |milestone| - @milestones.each do |milestone|
%li %li
= link_to project_filter_path(milestone_id: milestone.id) do = link_to page_filter_path(milestone_id: milestone.id) do
%strong= milestone.title %strong= milestone.title
%small.light= milestone.expires_at %small.light= milestone.expires_at
- if @project
.dropdown.inline.prepend-left-10.labels-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-tags
%span.light label:
- if params[:label_name].present?
%strong= params[:label_name]
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(label_name: nil) do
Any
- if @project.labels.any?
- @project.labels.order_by_name.each do |label|
%li
= link_to page_filter_path(label_name: label.name) do
= render_colored_label(label)
- else
%li
= link_to generate_project_labels_path(@project, redirect: request.original_url), method: :post do
%i.fa.fa-plus-circle
Create default labels
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
.side-filters
= form_tag project_entities_path, method: 'get' do
- if current_user
%fieldset
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if params[:scope] == 'all')}
= link_to project_filter_path(scope: 'all') do
Everyone's
%span.pull-right
= authorized_entities_count(current_user, entity, @project)
%li{class: ("active" if params[:scope] == 'assigned-to-me')}
= link_to project_filter_path(scope: 'assigned-to-me') do
Assigned to me
%span.pull-right
= assigned_entities_count(current_user, entity, @project)
%li{class: ("active" if params[:scope] == 'created-by-me')}
= link_to project_filter_path(scope: 'created-by-me') do
Created by me
%span.pull-right
= authored_entities_count(current_user, entity, @project)
%fieldset
%legend State
%ul.nav.nav-pills
%li{class: ("active" if params[:state] == 'opened')}
= link_to project_filter_path(state: 'opened') do
Open
%li{class: ("active" if params[:state] == 'closed')}
= link_to project_filter_path(state: 'closed') do
Closed
%li{class: ("active" if params[:state] == 'all')}
= link_to project_filter_path(state: 'all') do
All
- if defined?(labels)
%fieldset
%legend
Labels
%small.pull-right
= link_to project_labels_path(@project), class: 'light' do
%i.fa.fa-pencil-square-o
%ul.nav.nav-pills.nav-stacked.nav-small.labels-filter
- @project.labels.order_by_name.each do |label|
%li{class: label_filter_class(label.name)}
= link_to labels_filter_path(label.name) do
= render_colored_label(label)
- if selected_label?(label.name)
.pull-right
%i.fa.fa-times
- if @project.labels.empty?
.light-well
Create first label at
= link_to 'labels page', project_labels_path(@project)
%br
or #{link_to 'generate', generate_project_labels_path(@project, redirect: redirect), method: :post} default set of labels
%fieldset
- if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any?
= link_to project_entities_path, class: 'cgray pull-right' do
%i.fa.fa-times
%strong Clear filter
...@@ -8,15 +8,15 @@ ...@@ -8,15 +8,15 @@
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu
%li %li
= link_to project_filter_path(sort: 'newest') do = link_to page_filter_path(sort: 'newest') do
= sort_title_recently_created = sort_title_recently_created
= link_to project_filter_path(sort: 'oldest') do = link_to page_filter_path(sort: 'oldest') do
= sort_title_oldest_created = sort_title_oldest_created
= link_to project_filter_path(sort: 'recently_updated') do = link_to page_filter_path(sort: 'recently_updated') do
= sort_title_recently_updated = sort_title_recently_updated
= link_to project_filter_path(sort: 'last_updated') do = link_to page_filter_path(sort: 'last_updated') do
= sort_title_oldest_updated = sort_title_oldest_updated
= link_to project_filter_path(sort: 'milestone_due_soon') do = link_to page_filter_path(sort: 'milestone_due_soon') do
Milestone due soon Milestone due soon
= link_to project_filter_path(sort: 'milestone_due_later') do = link_to page_filter_path(sort: 'milestone_due_later') do
Milestone due later Milestone due later
...@@ -3,4 +3,6 @@ if Rails.env == 'development' ...@@ -3,4 +3,6 @@ if Rails.env == 'development'
# initialization is skipped so trigger it # initialization is skipped so trigger it
Rack::MiniProfilerRails.initialize!(Rails.application) Rack::MiniProfilerRails.initialize!(Rails.application)
Rack::MiniProfiler.config.position = 'right'
Rack::MiniProfiler.config.start_hidden = true
end end
Doorkeeper.configure do
# Change the ORM that doorkeeper will use.
# Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
# Put your resource owner authentication logic here.
# Example implementation:
current_user || redirect_to(new_user_session_url)
end
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
# admin_authenticator do
# # Put your admin authentication logic here.
# # Example implementation:
# Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
# end
# Authorization Code expiration time (default 10 minutes).
# authorization_code_expires_in 10.minutes
# Access token expiration time (default 2 hours).
# If you want to disable expiration, set this to nil.
# access_token_expires_in 2.hours
# Reuse access token for the same resource owner within an application (disabled by default)
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
# reuse_access_token
# Issue access tokens with refresh token (disabled by default)
use_refresh_token
# Provide support for an owner to be assigned to each registered application (disabled by default)
# Optional parameter :confirmation => true (default false) if you want to enforce ownership of
# a registered application
# Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
enable_application_owner :confirmation => true
# Define access token scopes for your provider
# For more information go to
# https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
default_scopes :api
#optional_scopes :write, :update
# Change the way client credentials are retrieved from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
# falls back to the `:client_id` and `:client_secret` params from the `params` object.
# Check out the wiki for more information on customization
# client_credentials :from_basic, :from_params
# Change the way access token is authenticated from the request object.
# By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
# falls back to the `:access_token` or `:bearer_token` params from the `params` object.
# Check out the wiki for more information on customization
access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param
# Change the native redirect uri for client apps
# When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
# The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
# (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
#
native_redirect_uri nil#'urn:ietf:wg:oauth:2.0:oob'
# Specify what grant flows are enabled in array of Strings. The valid
# strings and the flows they enable are:
#
# "authorization_code" => Authorization Code Grant Flow
# "implicit" => Implicit Grant Flow
# "password" => Resource Owner Password Credentials Grant Flow
# "client_credentials" => Client Credentials Grant Flow
#
# If not specified, Doorkeeper enables all the four grant flows.
#
# grant_flows %w(authorization_code implicit password client_credentials)
# Under some circumstances you might want to have applications auto-approved,
# so that the user skips the authorization step.
# For example if dealing with trusted a application.
# skip_authorization do |resource_owner, client|
# client.superapp? or resource_owner.admin?
# end
# WWW-Authenticate Realm (default "Doorkeeper").
# realm "Doorkeeper"
# Allow dynamic query parameters (disabled by default)
# Some applications require dynamic query parameters on their request_uri
# set to true if you want this to be allowed
# wildcard_redirect_uri false
end
en:
activerecord:
errors:
models:
application:
attributes:
redirect_uri:
fragment_present: 'cannot contain a fragment.'
invalid_uri: 'must be a valid URI.'
relative_uri: 'must be an absolute URI.'
mongoid:
errors:
models:
application:
attributes:
redirect_uri:
fragment_present: 'cannot contain a fragment.'
invalid_uri: 'must be a valid URI.'
relative_uri: 'must be an absolute URI.'
mongo_mapper:
errors:
models:
application:
attributes:
redirect_uri:
fragment_present: 'cannot contain a fragment.'
invalid_uri: 'must be a valid URI.'
relative_uri: 'must be an absolute URI.'
doorkeeper:
errors:
messages:
# Common error messages
invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
invalid_redirect_uri: 'The redirect uri included is not valid.'
unauthorized_client: 'The client is not authorized to perform this request using this method.'
access_denied: 'The resource owner or authorization server denied the request.'
invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
#configuration error messages
credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'
# Access grant errors
unsupported_response_type: 'The authorization server does not support this response type.'
# Access token errors
invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
# Password Access token errors
invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found'
invalid_token:
revoked: "The access token was revoked"
expired: "The access token expired"
unknown: "The access token is invalid"
scopes:
api: Access your API
flash:
applications:
create:
notice: 'Application created.'
destroy:
notice: 'Application deleted.'
update:
notice: 'Application updated.'
authorized_applications:
destroy:
notice: 'Application revoked.'
...@@ -2,6 +2,11 @@ require 'sidekiq/web' ...@@ -2,6 +2,11 @@ require 'sidekiq/web'
require 'api/api' require 'api/api'
Gitlab::Application.routes.draw do Gitlab::Application.routes.draw do
use_doorkeeper do
controllers :applications => 'oauth/applications',
:authorized_applications => 'oauth/authorized_applications',
:authorizations => 'oauth/authorizations'
end
# #
# Search # Search
# #
...@@ -122,6 +127,7 @@ Gitlab::Application.routes.draw do ...@@ -122,6 +127,7 @@ Gitlab::Application.routes.draw do
member do member do
get :history get :history
get :design get :design
get :applications
put :reset_private_token put :reset_private_token
put :update_username put :update_username
...@@ -219,6 +225,7 @@ Gitlab::Application.routes.draw do ...@@ -219,6 +225,7 @@ Gitlab::Application.routes.draw do
resources :raw, only: [:show], constraints: {id: /.+/} resources :raw, only: [:show], constraints: {id: /.+/}
resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ }
resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do
# Cannot be GET to differentiate from GET paths that end in preview.
post :preview, on: :member post :preview, on: :member
end end
resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new' resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new'
......
...@@ -16,14 +16,13 @@ Gitlab::Seeder.quiet do ...@@ -16,14 +16,13 @@ Gitlab::Seeder.quiet do
(1..5).each do |i| (1..5).each do |i|
begin begin
User.seed(:id, [ User.seed do |s|
id: i + 10, s.username = "user#{i}"
username: "user#{i}", s.name = "User #{i}"
name: "User #{i}", s.email = "user#{i}@example.com"
email: "user#{i}@example.com", s.confirmed_at = DateTime.now
confirmed_at: DateTime.now, s.password = '12345678'
password: '12345678' end
])
print '.' print '.'
rescue ActiveRecord::RecordNotSaved rescue ActiveRecord::RecordNotSaved
print 'F' print 'F'
......
...@@ -14,6 +14,10 @@ SELECT provider, extern_uid, id FROM users ...@@ -14,6 +14,10 @@ SELECT provider, extern_uid, id FROM users
WHERE provider IS NOT NULL WHERE provider IS NOT NULL
eos eos
if index_exists?(:users, ["extern_uid", "provider"])
remove_index :users, ["extern_uid", "provider"]
end
remove_column :users, :extern_uid remove_column :users, :extern_uid
remove_column :users, :provider remove_column :users, :provider
end end
...@@ -34,5 +38,9 @@ eos ...@@ -34,5 +38,9 @@ eos
end end
drop_table :identities drop_table :identities
unless index_exists?(:users, ["extern_uid", "provider"])
add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
end
end end
end end
class CreateDoorkeeperTables < ActiveRecord::Migration
def change
create_table :oauth_applications do |t|
t.string :name, null: false
t.string :uid, null: false
t.string :secret, null: false
t.text :redirect_uri, null: false
t.string :scopes, null: false, default: ''
t.timestamps
end
add_index :oauth_applications, :uid, unique: true
create_table :oauth_access_grants do |t|
t.integer :resource_owner_id, null: false
t.integer :application_id, null: false
t.string :token, null: false
t.integer :expires_in, null: false
t.text :redirect_uri, null: false
t.datetime :created_at, null: false
t.datetime :revoked_at
t.string :scopes
end
add_index :oauth_access_grants, :token, unique: true
create_table :oauth_access_tokens do |t|
t.integer :resource_owner_id
t.integer :application_id
t.string :token, null: false
t.string :refresh_token
t.integer :expires_in
t.datetime :revoked_at
t.datetime :created_at, null: false
t.string :scopes
end
add_index :oauth_access_tokens, :token, unique: true
add_index :oauth_access_tokens, :resource_owner_id
add_index :oauth_access_tokens, :refresh_token, unique: true
end
end
class AddOwnerToApplication < ActiveRecord::Migration
def change
add_column :oauth_applications, :owner_id, :integer, null: true
add_column :oauth_applications, :owner_type, :string, null: true
add_index :oauth_applications, [:owner_id, :owner_type]
end
end
\ No newline at end of file
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20141212124604) do ActiveRecord::Schema.define(version: 20141217125223) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -262,9 +262,9 @@ ActiveRecord::Schema.define(version: 20141212124604) do ...@@ -262,9 +262,9 @@ ActiveRecord::Schema.define(version: 20141212124604) do
t.datetime "updated_at" t.datetime "updated_at"
t.string "type" t.string "type"
t.string "description", default: "", null: false t.string "description", default: "", null: false
t.string "avatar"
t.string "ldap_cn" t.string "ldap_cn"
t.integer "ldap_access" t.integer "ldap_access"
t.string "avatar"
t.boolean "membership_lock", default: false t.boolean "membership_lock", default: false
end end
...@@ -297,6 +297,49 @@ ActiveRecord::Schema.define(version: 20141212124604) do ...@@ -297,6 +297,49 @@ ActiveRecord::Schema.define(version: 20141212124604) do
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
create_table "oauth_access_grants", force: true do |t|
t.integer "resource_owner_id", null: false
t.integer "application_id", null: false
t.string "token", null: false
t.integer "expires_in", null: false
t.text "redirect_uri", null: false
t.datetime "created_at", null: false
t.datetime "revoked_at"
t.string "scopes"
end
add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
create_table "oauth_access_tokens", force: true do |t|
t.integer "resource_owner_id"
t.integer "application_id"
t.string "token", null: false
t.string "refresh_token"
t.integer "expires_in"
t.datetime "revoked_at"
t.datetime "created_at", null: false
t.string "scopes"
end
add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
create_table "oauth_applications", force: true do |t|
t.string "name", null: false
t.string "uid", null: false
t.string "secret", null: false
t.text "redirect_uri", null: false
t.string "scopes", default: "", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "owner_id"
t.string "owner_type"
end
add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
create_table "project_group_links", force: true do |t| create_table "project_group_links", force: true do |t|
t.integer "project_id", null: false t.integer "project_id", null: false
t.integer "group_id", null: false t.integer "group_id", null: false
...@@ -326,8 +369,8 @@ ActiveRecord::Schema.define(version: 20141212124604) do ...@@ -326,8 +369,8 @@ ActiveRecord::Schema.define(version: 20141212124604) do
t.boolean "archived", default: false, null: false t.boolean "archived", default: false, null: false
t.string "import_status" t.string "import_status"
t.float "repository_size", default: 0.0 t.float "repository_size", default: 0.0
t.integer "star_count", default: 0, null: false
t.text "merge_requests_template" t.text "merge_requests_template"
t.integer "star_count", default: 0, null: false
t.boolean "merge_requests_rebase_enabled", default: false t.boolean "merge_requests_rebase_enabled", default: false
end end
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Operations](operations/README.md) Keeping GitLab up and running
## Contributor documentation ## Contributor documentation
......
...@@ -296,9 +296,9 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -296,9 +296,9 @@ GitLab Shell is an SSH access and repository management software developed speci
# When done you see 'Administrator account created:' # When done you see 'Administrator account created:'
**Note:** You can set the Administrator password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD`, eg.: **Note:** You can set the Administrator/root password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD` as seen below. If you don't set the password (and it is set to the default one) please wait with exposing GitLab to the public internet until the installation is done and you've logged into the server the first time. During the first login you'll be forced to change the default password.
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=newpassword sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword
### Install Init Script ### Install Init Script
...@@ -385,15 +385,17 @@ NOTE: Supply `SANITIZE=true` environment variable to `gitlab:check` to omit proj ...@@ -385,15 +385,17 @@ NOTE: Supply `SANITIZE=true` environment variable to `gitlab:check` to omit proj
### Initial Login ### Initial Login
Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created an admin account for you. You can use it to log in: Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created a default admin account for you. You can use it to log in:
root root
5iveL!fe 5iveL!fe
**Important Note:** Please go over to your profile page and immediately change the password, so nobody can access your GitLab by using this login information later on. **Important Note:** On login you'll be prompted to change the password.
**Enjoy!** **Enjoy!**
You can use `sudo service gitlab start` and `sudo service gitlab stop` to start and stop GitLab.
## Advanced Setup Tips ## Advanced Setup Tips
### Using HTTPS ### Using HTTPS
......
...@@ -7,6 +7,7 @@ OmniAuth does not prevent standard GitLab authentication or LDAP (if configured) ...@@ -7,6 +7,7 @@ OmniAuth does not prevent standard GitLab authentication or LDAP (if configured)
- [Initial OmniAuth Configuration](#initial-omniauth-configuration) - [Initial OmniAuth Configuration](#initial-omniauth-configuration)
- [Supported Providers](#supported-providers) - [Supported Providers](#supported-providers)
- [Enable OmniAuth for an Existing User](#enable-omniauth-for-an-existing-user) - [Enable OmniAuth for an Existing User](#enable-omniauth-for-an-existing-user)
- [OmniAuth configuration sample when using Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master#omniauth-google-twitter-github-login)
## Initial OmniAuth Configuration ## Initial OmniAuth Configuration
......
# GitLab operations
- [Sidekiq MemoryKiller](sidekiq_memory_killer.md)
# Sidekiq MemoryKiller
The GitLab Rails application code suffers from memory leaks. For web requests
this problem is made manageable using
[unicorn-worker-killer](https://github.com/kzk/unicorn-worker-killer) which
restarts Unicorn worker processes in between requests when needed. The Sidekiq
MemoryKiller applies the same approach to the Sidekiq processes used by GitLab
to process background jobs.
Unlike unicorn-worker-killer, which is enabled by default for all GitLab
installations since GitLab 6.4, the Sidekiq MemoryKiller is enabled by default
_only_ for Omnibus packages. The reason for this is that the MemoryKiller
relies on Runit to restart Sidekiq after a memory-induced shutdown and GitLab
installations from source do not all use Runit or an equivalent.
With the default settings, the MemoryKiller will cause a Sidekiq restart no
more often than once every 15 minutes, with the restart causing about one
minute of delay for incoming background jobs.
## Configuring the MemoryKiller
The MemoryKiller is controlled using environment variables.
- `SIDEKIQ_MEMORY_KILLER_MAX_RSS`: if this variable is set, and its value is
greater than 0, then after each Sidekiq job, the MemoryKiller will check the
RSS of the Sidekiq process that executed the job. If the RSS of the Sidekiq
process (expressed in kilobytes) exceeds SIDEKIQ_MEMORY_KILLER_MAX_RSS, a
delayed shutdown is triggered. The default value for Omnibus packages is set
[in the omnibus-gitlab
repository](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb).
- `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`: defaults 900 seconds (15 minutes). When
a shutdown is triggered, the Sidekiq process will keep working normally for
another 15 minutes.
- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT`: defaults to 30 seconds. When the grace
time has expired, the MemoryKiller tells Sidekiq to stop accepting new jobs.
Existing jobs get 30 seconds to finish. After that, the MemoryKiller tells
Sidekiq to shut down, and an external supervision mechanism (e.g. Runit) must
restart Sidekiq.
# Rake tasks
- [Backup restore](backup_restore.md) - [Backup restore](backup_restore.md)
- [Cleanup](cleanup.md) - [Cleanup](cleanup.md)
- [Features](features.md)
- [Maintenance](maintenance.md) and self-checks - [Maintenance](maintenance.md) and self-checks
- [User management](user_management.md) - [User management](user_management.md)
- [Web hooks](web_hooks.md) - [Web hooks](web_hooks.md)
......
...@@ -100,9 +100,9 @@ List any major changes here, so the user is aware of them before starting to upg ...@@ -100,9 +100,9 @@ List any major changes here, so the user is aware of them before starting to upg
- Web server changes - Web server changes
- File structure changes - File structure changes
#### 1. Make backup #### 1. Stop server
#### 2. Stop server #### 2. Make backup
#### 3. Do users need to update dependencies like `git`? #### 3. Do users need to update dependencies like `git`?
...@@ -200,7 +200,7 @@ Add to your local `gitlab-ci/.git/config`: ...@@ -200,7 +200,7 @@ Add to your local `gitlab-ci/.git/config`:
# **4 workdays before release - Release RC1** # **4 workdays before release - Release RC1**
### **1. Determine QA person ### **1. Determine QA person**
Notify person of QA day. Notify person of QA day.
...@@ -215,6 +215,7 @@ It is important to do this as soon as possible, so we can catch any errors befor ...@@ -215,6 +215,7 @@ It is important to do this as soon as possible, so we can catch any errors befor
### **3. Prepare the blog post** ### **3. Prepare the blog post**
- Start with a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) and fill it out. - Start with a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) and fill it out.
- Make sure the blog post contains information about the GitLab CI release.
- Check the changelog of CE and EE for important changes. - Check the changelog of CE and EE for important changes.
- Also check the CI changelog - Also check the CI changelog
- Add a proposed tweet text to the blog post WIP MR description. - Add a proposed tweet text to the blog post WIP MR description.
...@@ -224,8 +225,8 @@ It is important to do this as soon as possible, so we can catch any errors befor ...@@ -224,8 +225,8 @@ It is important to do this as soon as possible, so we can catch any errors befor
- Create WIP MR for adding MVP to MVP page on website - Create WIP MR for adding MVP to MVP page on website
- Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. - Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible.
- Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master) - Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master)
- Assign to one reviewer who will fix spelling issues by editing the branch (can use the online editor) - Assign to one reviewer who will fix spelling issues by editing the branch (either with a git client or by using the online editor)
- After the reviewer is finished the whole team will be mentioned to give their suggestions via line comments - Comment to the reviewer: '@person Please mention the whole team as soon as you are done (3 workdays before release at the latest)'
### **4. Create a regressions issue** ### **4. Create a regressions issue**
...@@ -238,7 +239,7 @@ The release manager will comment here about the plans for patch releases. ...@@ -238,7 +239,7 @@ The release manager will comment here about the plans for patch releases.
Assign the issue to the release manager and /cc all the core-team members active on the issue tracker. If there are any known bugs in the release add them immediately. Assign the issue to the release manager and /cc all the core-team members active on the issue tracker. If there are any known bugs in the release add them immediately.
### **4. Tweet** ### **5. Tweet**
Tweet about the RC release: Tweet about the RC release:
...@@ -246,6 +247,10 @@ Tweet about the RC release: ...@@ -246,6 +247,10 @@ Tweet about the RC release:
# **1 workdays before release - Preparation** # **1 workdays before release - Preparation**
### **0. Doublecheck blog post**
Doublecheck the everyone has been mentioned in the blog post.
### **1. Pre QA merge** ### **1. Pre QA merge**
Merge CE into EE before doing the QA. Merge CE into EE before doing the QA.
...@@ -265,7 +270,7 @@ Create an issue with description of a problem, if it is quick fix fix it yoursel ...@@ -265,7 +270,7 @@ Create an issue with description of a problem, if it is quick fix fix it yoursel
**NOTE** If there is a problem that cannot be fixed in a timely manner, reverting the feature is an option! If the feature is reverted, **NOTE** If there is a problem that cannot be fixed in a timely manner, reverting the feature is an option! If the feature is reverted,
create an issue about it in order to discuss the next steps after the release. create an issue about it in order to discuss the next steps after the release.
# **22nd - Release CE, EE and CI** # **Workday before release - Create Omnibus tags and build packages**
**Make sure EE `x-x-stable-ee` has latest changes from CE `x-x-stable`** **Make sure EE `x-x-stable-ee` has latest changes from CE `x-x-stable`**
...@@ -302,20 +307,24 @@ Follow the [release doc in the Omnibus repository](https://gitlab.com/gitlab-org ...@@ -302,20 +307,24 @@ Follow the [release doc in the Omnibus repository](https://gitlab.com/gitlab-org
This can happen before tagging because Omnibus uses tags in its own repo and SHA1's to refer to the GitLab codebase. This can happen before tagging because Omnibus uses tags in its own repo and SHA1's to refer to the GitLab codebase.
### **4. Publish packages for new release** # **22nd - Release CE, EE and CI**
### **1. Publish packages for new release**
Update `downloads/index.html` and `downloads/archive/index.html` in `www-gitlab-com` repository. Update `downloads/index.html` and `downloads/archive/index.html` in `www-gitlab-com` repository.
### **5. Publish blog for new release** ### **2. Publish blog for new release**
Merge the [blog merge request](#1-prepare-the-blog-post) in `www-gitlab-com` repository. Merge the [blog merge request](#1-prepare-the-blog-post) in `www-gitlab-com` repository.
### **6. Tweet to blog** ### **3. Tweet to blog**
Send out a tweet to share the good news with the world. Send out a tweet to share the good news with the world.
List the most important features and link to the blog post. List the most important features and link to the blog post.
Proposed tweet for CE "GitLab X.X is released! It brings *** <link-to-blogpost>" Proposed tweet "Release of GitLab X.X & CI Y.Y! FEATURE, FEATURE and FEATURE <link-to-blogpost> #gitlab"
Consider creating a post on Hacker News.
# **1 workday after release - Update GitLab.com** # **1 workday after release - Update GitLab.com**
......
...@@ -13,7 +13,7 @@ sudo service gitlab stop ...@@ -13,7 +13,7 @@ sudo service gitlab stop
git clone https://github.com/gitlabhq/mysql-postgresql-converter.git git clone https://github.com/gitlabhq/mysql-postgresql-converter.git
cd mysql-postgresql-converter cd mysql-postgresql-converter
mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production -p
python db_converter.py databasename.mysql databasename.psql python db_converter.py databasename.mysql databasename.psql
# Import the database dump as the application database user # Import the database dump as the application database user
...@@ -94,7 +94,7 @@ sudo -u git -H mv tmp/backups/TIMESTAMP_gitlab_backup.tar tmp/backups/postgresql ...@@ -94,7 +94,7 @@ sudo -u git -H mv tmp/backups/TIMESTAMP_gitlab_backup.tar tmp/backups/postgresql
# Create a separate database dump with PostgreSQL compatibility # Create a separate database dump with PostgreSQL compatibility
cd tmp/backups/postgresql cd tmp/backups/postgresql
sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production -p
# Clone the database converter # Clone the database converter
sudo -u git -H git clone https://github.com/gitlabhq/mysql-postgresql-converter.git sudo -u git -H git clone https://github.com/gitlabhq/mysql-postgresql-converter.git
......
...@@ -23,7 +23,7 @@ If you have local changes to your GitLab repository the script will stash them a ...@@ -23,7 +23,7 @@ If you have local changes to your GitLab repository the script will stash them a
## 2. Run GitLab upgrade tool ## 2. Run GitLab upgrade tool
Note: GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) Note: GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX) while 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
# Starting with GitLab version 7.0 upgrader script has been moved to bin directory # Starting with GitLab version 7.0 upgrader script has been moved to bin directory
cd /home/git/gitlab cd /home/git/gitlab
......
...@@ -2,16 +2,16 @@ FROM ubuntu:14.04 ...@@ -2,16 +2,16 @@ FROM ubuntu:14.04
# Install required packages # Install required packages
RUN apt-get update -q \ RUN apt-get update -q \
&& DEBIAN_FRONTEND=noninteractive apt-get install -qy \ && DEBIAN_FRONTEND=noninteractive apt-get install -qy --no-install-recommends \
ca-certificates \
openssh-server \ openssh-server \
wget \ wget
&& apt-get clean
# Download & Install GitLab # Download & Install GitLab
# If the Omnibus package version below is outdated please contribute a merge request to update it. # If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it. # If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \ RUN TMP_FILE=$(mktemp); \
wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.5.2-omnibus.5.2.1.ci-1_amd64.deb \ wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.5.3-omnibus.5.2.1.ci-1_amd64.deb \
&& dpkg -i $TMP_FILE \ && dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE && rm -f $TMP_FILE
......
...@@ -28,7 +28,6 @@ Feature: Explore Groups ...@@ -28,7 +28,6 @@ Feature: Explore Groups
Given group "TestGroup" has internal project "Internal" Given group "TestGroup" has internal project "Internal"
When I sign in as a user When I sign in as a user
And I visit group "TestGroup" issues page And I visit group "TestGroup" issues page
And I change filter to Everyone's
Then I should see project "Internal" items Then I should see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
...@@ -36,7 +35,6 @@ Feature: Explore Groups ...@@ -36,7 +35,6 @@ Feature: Explore Groups
Given group "TestGroup" has internal project "Internal" Given group "TestGroup" has internal project "Internal"
When I sign in as a user When I sign in as a user
And I visit group "TestGroup" merge requests page And I visit group "TestGroup" merge requests page
And I change filter to Everyone's
Then I should see project "Internal" items Then I should see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
...@@ -94,7 +92,6 @@ Feature: Explore Groups ...@@ -94,7 +92,6 @@ Feature: Explore Groups
Given group "TestGroup" has public project "Community" Given group "TestGroup" has public project "Community"
When I sign in as a user When I sign in as a user
And I visit group "TestGroup" issues page And I visit group "TestGroup" issues page
And I change filter to Everyone's
Then I should see project "Community" items Then I should see project "Community" items
And I should see project "Internal" items And I should see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
...@@ -104,7 +101,6 @@ Feature: Explore Groups ...@@ -104,7 +101,6 @@ Feature: Explore Groups
Given group "TestGroup" has public project "Community" Given group "TestGroup" has public project "Community"
When I sign in as a user When I sign in as a user
And I visit group "TestGroup" merge requests page And I visit group "TestGroup" merge requests page
And I change filter to Everyone's
Then I should see project "Community" items Then I should see project "Community" items
And I should see project "Internal" items And I should see project "Internal" items
And I should not see project "Enterprise" items And I should not see project "Enterprise" items
......
...@@ -71,6 +71,20 @@ Feature: Profile ...@@ -71,6 +71,20 @@ Feature: Profile
And I click on my profile picture And I click on my profile picture
Then I should see my user page Then I should see my user page
Scenario: I can manage application
Given I visit profile applications page
Then I click on new application button
And I should see application form
Then I fill application form out and submit
And I see application
Then I click edit
And I see edit application form
Then I change name of application and submit
And I see that application was changed
Then I visit profile applications page
And I click to remove application
Then I see that application is removed
@javascript @javascript
Scenario: I change my application theme Scenario: I change my application theme
Given I visit profile design page Given I visit profile design page
......
...@@ -22,7 +22,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps ...@@ -22,7 +22,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
end end
step 'submit form with new group info' do step 'submit form with new group info' do
fill_in 'group_name', with: 'gitlab' fill_in 'group_path', with: 'gitlab'
fill_in 'group_description', with: 'Group description' fill_in 'group_description', with: 'Group description'
click_button "Create group" click_button "Create group"
end end
......
...@@ -35,14 +35,20 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps ...@@ -35,14 +35,20 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
end end
step 'I click "Authored by me" link' do step 'I click "Authored by me" link' do
within ".scope-filter" do within ".assignee-filter" do
click_link 'Created by me' click_link "Any"
end
within ".author-filter" do
click_link current_user.name
end end
end end
step 'I click "All" link' do step 'I click "All" link' do
within ".scope-filter" do within ".author-filter" do
click_link "Everyone's" click_link "Any"
end
within ".assignee-filter" do
click_link "Any"
end end
end end
......
...@@ -39,14 +39,20 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps ...@@ -39,14 +39,20 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
end end
step 'I click "Authored by me" link' do step 'I click "Authored by me" link' do
within ".scope-filter" do within ".assignee-filter" do
click_link 'Created by me' click_link "Any"
end
within ".author-filter" do
click_link current_user.name
end end
end end
step 'I click "All" link' do step 'I click "All" link' do
within ".scope-filter" do within ".author-filter" do
click_link "Everyone's" click_link "Any"
end
within ".assignee-filter" do
click_link "Any"
end end
end end
......
...@@ -104,7 +104,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -104,7 +104,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
end end
step 'submit form with new group "Samurai" info' do step 'submit form with new group "Samurai" info' do
fill_in 'group_name', with: 'Samurai' fill_in 'group_path', with: 'Samurai'
fill_in 'group_description', with: 'Tokugawa Shogunate' fill_in 'group_description', with: 'Tokugawa Shogunate'
click_button "Create group" click_button "Create group"
end end
...@@ -116,7 +116,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -116,7 +116,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I should see newly created group "Samurai"' do step 'I should see newly created group "Samurai"' do
page.should have_content "Samurai" page.should have_content "Samurai"
page.should have_content "Tokugawa Shogunate" page.should have_content "Tokugawa Shogunate"
page.should have_content "Currently you are only seeing events from the"
end end
Then 'I should be redirected to group page' do Then 'I should be redirected to group page' do
...@@ -132,12 +131,13 @@ class Spinach::Features::Groups < Spinach::FeatureSteps ...@@ -132,12 +131,13 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
step 'I change group "Owned" name to "new-name"' do step 'I change group "Owned" name to "new-name"' do
fill_in 'group_name', with: 'new-name' fill_in 'group_name', with: 'new-name'
fill_in 'group_path', with: 'new-name'
click_button "Save group" click_button "Save group"
end end
step 'I should see new group "Owned" name' do step 'I should see new group "Owned" name' do
within ".navbar-gitlab" do within ".navbar-gitlab" do
page.should have_content "group: new-name" page.should have_content "new-name"
end end
end end
......
...@@ -221,4 +221,54 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -221,4 +221,54 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
step 'I should see groups I belong to' do step 'I should see groups I belong to' do
page.should have_css('.profile-groups-avatars', visible: true) page.should have_css('.profile-groups-avatars', visible: true)
end end
step 'I click on new application button' do
click_on 'New Application'
end
step 'I should see application form' do
page.should have_content "New application"
end
step 'I fill application form out and submit' do
fill_in :doorkeeper_application_name, with: 'test'
fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com'
click_on "Submit"
end
step 'I see application' do
page.should have_content "Application: test"
page.should have_content "Application Id"
page.should have_content "Secret"
end
step 'I click edit' do
click_on "Edit"
end
step 'I see edit application form' do
page.should have_content "Edit application"
end
step 'I change name of application and submit' do
page.should have_content "Edit application"
fill_in :doorkeeper_application_name, with: 'test_changed'
click_on "Submit"
end
step 'I see that application was changed' do
page.should have_content "test_changed"
page.should have_content "Application Id"
page.should have_content "Secret"
end
step 'I click to remove application' do
within '.oauth-applications' do
click_on "Destroy"
end
end
step "I see that application is removed" do
page.find(".oauth-applications").should_not have_content "test_changed"
end
end end
...@@ -78,14 +78,14 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps ...@@ -78,14 +78,14 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
end end
step 'I click side-by-side diff button' do step 'I click side-by-side diff button' do
click_link "Side-by-side Diff" click_link "Side-by-side"
end end
step 'I see side-by-side diff button' do step 'I see side-by-side diff button' do
page.should have_content "Side-by-side Diff" page.should have_content "Side-by-side"
end end
step 'I see inline diff button' do step 'I see inline diff button' do
page.should have_content "Inline Diff" page.should have_content "Inline"
end end
end end
...@@ -3,7 +3,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps ...@@ -3,7 +3,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
include SharedPaths include SharedPaths
step 'fill project form with valid data' do step 'fill project form with valid data' do
fill_in 'project_name', with: 'Empty' fill_in 'project_path', with: 'Empty'
click_button "Create project" click_button "Create project"
end end
......
...@@ -57,9 +57,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -57,9 +57,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click link "Close"' do step 'I click link "Close"' do
within '.page-title' do first(:css, '.close-mr-link').click
click_link "Close"
end
end end
step 'I submit new merge request "Wiki Feature"' do step 'I submit new merge request "Wiki Feature"' do
...@@ -111,6 +109,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -111,6 +109,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click on the commit in the merge request' do step 'I click on the commit in the merge request' do
within '.merge-request-tabs' do
click_link 'Commits'
end
within '.mr-commits' do within '.mr-commits' do
click_link Commit.truncate_sha(sample_commit.id) click_link Commit.truncate_sha(sample_commit.id)
end end
...@@ -181,13 +183,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -181,13 +183,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click link "Reopen"' do step 'I click link "Reopen"' do
within '.page-title' do first(:css, '.reopen-mr-link').click
click_link "Reopen"
end
end end
step 'I should see reopened merge request "Bug NS-04"' do step 'I should see reopened merge request "Bug NS-04"' do
within '.state-label' do within '.issue-box' do
page.should have_content "Open" page.should have_content "Open"
end end
end end
...@@ -275,7 +275,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -275,7 +275,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click Side-by-side Diff tab' do step 'I click Side-by-side Diff tab' do
click_link 'Side-by-side Diff' click_link 'Side-by-side'
end end
step 'I should see comments on the side-by-side diff page' do step 'I should see comments on the side-by-side diff page' do
......
...@@ -2,7 +2,7 @@ module SharedActiveTab ...@@ -2,7 +2,7 @@ module SharedActiveTab
include Spinach::DSL include Spinach::DSL
def ensure_active_main_tab(content) def ensure_active_main_tab(content)
find('.main-nav li.active').should have_content(content) find('.nav-sidebar > li.active').should have_content(content)
end end
def ensure_active_sub_tab(content) def ensure_active_sub_tab(content)
...@@ -10,11 +10,11 @@ module SharedActiveTab ...@@ -10,11 +10,11 @@ module SharedActiveTab
end end
def ensure_active_sub_nav(content) def ensure_active_sub_nav(content)
find('div.content ul.nav-stacked-menu li.active').should have_content(content) find('.sidebar-subnav > li.active').should have_content(content)
end end
step 'no other main tabs should be active' do step 'no other main tabs should be active' do
page.should have_selector('.main-nav li.active', count: 1) page.should have_selector('.nav-sidebar > li.active', count: 1)
end end
step 'no other sub tabs should be active' do step 'no other sub tabs should be active' do
...@@ -22,7 +22,7 @@ module SharedActiveTab ...@@ -22,7 +22,7 @@ module SharedActiveTab
end end
step 'no other sub navs should be active' do step 'no other sub navs should be active' do
page.should have_selector('div.content ul.nav-stacked-menu li.active', count: 1) page.should have_selector('.sidebar-subnav > li.active', count: 1)
end end
step 'the active main tab should be Home' do step 'the active main tab should be Home' do
......
...@@ -2,7 +2,7 @@ module SharedIssuable ...@@ -2,7 +2,7 @@ module SharedIssuable
include Spinach::DSL include Spinach::DSL
def edit_issuable def edit_issuable
find('.issue-btn-group').click_link 'Edit' find(:css, '.issuable-edit').click
end end
step 'I click link "Edit" for the merge request' do step 'I click link "Edit" for the merge request' do
......
...@@ -102,6 +102,10 @@ module SharedPaths ...@@ -102,6 +102,10 @@ module SharedPaths
visit profile_path visit profile_path
end end
step 'I visit profile applications page' do
visit applications_profile_path
end
step 'I visit profile password page' do step 'I visit profile password page' do
visit edit_profile_password_path visit edit_profile_password_path
end end
......
...@@ -2,6 +2,7 @@ Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file} ...@@ -2,6 +2,7 @@ Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file}
module API module API
class API < Grape::API class API < Grape::API
include APIGuard
version 'v3', using: :path version 'v3', using: :path
rescue_from ActiveRecord::RecordNotFound do rescue_from ActiveRecord::RecordNotFound do
......
# Guard API with OAuth 2.0 Access Token
require 'rack/oauth2'
module APIGuard
extend ActiveSupport::Concern
included do |base|
# OAuth2 Resource Server Authentication
use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request|
# The authenticator only fetches the raw token string
# Must yield access token to store it in the env
request.access_token
end
helpers HelperMethods
install_error_responders(base)
end
# Helper Methods for Grape Endpoint
module HelperMethods
# Invokes the doorkeeper guard.
#
# If token is presented and valid, then it sets @current_user.
#
# If the token does not have sufficient scopes to cover the requred scopes,
# then it raises InsufficientScopeError.
#
# If the token is expired, then it raises ExpiredError.
#
# If the token is revoked, then it raises RevokedError.
#
# If the token is not found (nil), then it raises TokenNotFoundError.
#
# Arguments:
#
# scopes: (optional) scopes required for this guard.
# Defaults to empty array.
#
def doorkeeper_guard!(scopes: [])
if (access_token = find_access_token).nil?
raise TokenNotFoundError
else
case validate_access_token(access_token, scopes)
when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE
raise InsufficientScopeError.new(scopes)
when Oauth2::AccessTokenValidationService::EXPIRED
raise ExpiredError
when Oauth2::AccessTokenValidationService::REVOKED
raise RevokedError
when Oauth2::AccessTokenValidationService::VALID
@current_user = User.find(access_token.resource_owner_id)
end
end
end
def doorkeeper_guard(scopes: [])
if access_token = find_access_token
case validate_access_token(access_token, scopes)
when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE
raise InsufficientScopeError.new(scopes)
when Oauth2::AccessTokenValidationService::EXPIRED
raise ExpiredError
when Oauth2::AccessTokenValidationService::REVOKED
raise RevokedError
when Oauth2::AccessTokenValidationService::VALID
@current_user = User.find(access_token.resource_owner_id)
end
end
end
def current_user
@current_user
end
private
def find_access_token
@access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods)
end
def doorkeeper_request
@doorkeeper_request ||= ActionDispatch::Request.new(env)
end
def validate_access_token(access_token, scopes)
Oauth2::AccessTokenValidationService.validate(access_token, scopes: scopes)
end
end
module ClassMethods
# Installs the doorkeeper guard on the whole Grape API endpoint.
#
# Arguments:
#
# scopes: (optional) scopes required for this guard.
# Defaults to empty array.
#
def guard_all!(scopes: [])
before do
guard! scopes: scopes
end
end
private
def install_error_responders(base)
error_classes = [ MissingTokenError, TokenNotFoundError,
ExpiredError, RevokedError, InsufficientScopeError]
base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler
end
def oauth2_bearer_token_error_handler
Proc.new {|e|
response = case e
when MissingTokenError
Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new
when TokenNotFoundError
Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
:invalid_token,
"Bad Access Token.")
when ExpiredError
Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
:invalid_token,
"Token is expired. You can either do re-authorization or token refresh.")
when RevokedError
Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
:invalid_token,
"Token was revoked. You have to re-authorize from the user.")
when InsufficientScopeError
# FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2)
# does not include WWW-Authenticate header, which breaks the standard.
Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(
:insufficient_scope,
Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope],
{ :scope => e.scopes})
end
response.finish
}
end
end
#
# Exceptions
#
class MissingTokenError < StandardError; end
class TokenNotFoundError < StandardError; end
class ExpiredError < StandardError; end
class RevokedError < StandardError; end
class InsufficientScopeError < StandardError
attr_reader :scopes
def initialize(scopes)
@scopes = scopes
end
end
end
\ No newline at end of file
...@@ -11,7 +11,7 @@ module API ...@@ -11,7 +11,7 @@ module API
def current_user def current_user
private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
@current_user ||= User.find_by(authentication_token: private_token) @current_user ||= (User.find_by(authentication_token: private_token) || doorkeeper_guard)
unless @current_user && Gitlab::UserAccess.allowed?(@current_user) unless @current_user && Gitlab::UserAccess.allowed?(@current_user)
return nil return nil
......
...@@ -19,5 +19,19 @@ module Gitlab ...@@ -19,5 +19,19 @@ module Gitlab
return themes[id] return themes[id]
end end
def self.type_css_class_by_id(id)
types = {
BASIC => 'light_theme',
MARS => 'dark_theme',
MODERN => 'dark_theme',
GRAY => 'dark_theme',
COLOR => 'dark_theme'
}
id ||= Gitlab.config.gitlab.default_theme
types[id]
end
end end
end end
...@@ -585,10 +585,6 @@ namespace :gitlab do ...@@ -585,10 +585,6 @@ namespace :gitlab do
def gitlab_shell_patch_version def gitlab_shell_patch_version
Gitlab::Shell.version_required.split('.')[2].to_i Gitlab::Shell.version_required.split('.')[2].to_i
end end
def has_gitlab_shell3?
gitlab_shell_version.try(:start_with?, "v3.")
end
end end
...@@ -790,14 +786,14 @@ namespace :gitlab do ...@@ -790,14 +786,14 @@ namespace :gitlab do
end end
def sanitized_message(project) def sanitized_message(project)
if sanitize if should_sanitize?
"#{project.namespace_id.to_s.yellow}/#{project.id.to_s.yellow} ... " "#{project.namespace_id.to_s.yellow}/#{project.id.to_s.yellow} ... "
else else
"#{project.name_with_namespace.yellow} ... " "#{project.name_with_namespace.yellow} ... "
end end
end end
def sanitize def should_sanitize?
if ENV['SANITIZE'] == "true" if ENV['SANITIZE'] == "true"
true true
else else
......
...@@ -12,7 +12,7 @@ describe "Admin::Hooks", feature: true do ...@@ -12,7 +12,7 @@ describe "Admin::Hooks", feature: true do
describe "GET /admin/hooks" do describe "GET /admin/hooks" do
it "should be ok" do it "should be ok" do
visit admin_root_path visit admin_root_path
within ".main-nav" do within ".sidebar-wrapper" do
click_on "Hooks" click_on "Hooks"
end end
current_path.should == admin_hooks_path current_path.should == admin_hooks_path
......
...@@ -41,6 +41,7 @@ describe API, api: true do ...@@ -41,6 +41,7 @@ describe API, api: true do
describe ".current_user" do describe ".current_user" do
it "should return nil for an invalid token" do it "should return nil for an invalid token" do
env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token' env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
self.class.any_instance.stub(:doorkeeper_guard){ false }
current_user.should be_nil current_user.should be_nil
end end
......
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let!(:user) { create(:user) }
let!(:application) { Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "https://app.com", :owner => user) }
let!(:token) { Doorkeeper::AccessToken.create! :application_id => application.id, :resource_owner_id => user.id }
describe "when unauthenticated" do
it "returns authentication success" do
get api("/user"), :access_token => token.token
response.status.should == 200
end
end
describe "when token invalid" do
it "returns authentication error" do
get api("/user"), :access_token => "123a"
response.status.should == 401
end
end
describe "authorization by private token" do
it "returns authentication success" do
get api("/user", user)
response.status.should == 200
end
end
end
...@@ -198,8 +198,6 @@ describe API::API, api: true do ...@@ -198,8 +198,6 @@ describe API::API, api: true do
it 'should respond with 400 on failure' do it 'should respond with 400 on failure' do
post api("/projects/user/#{user.id}", admin) post api("/projects/user/#{user.id}", admin)
response.status.should == 400 response.status.should == 400
json_response['message']['creator'].should == ['can\'t be blank']
json_response['message']['namespace'].should == ['can\'t be blank']
json_response['message']['name'].should == [ json_response['message']['name'].should == [
'can\'t be blank', 'can\'t be blank',
'is too short (minimum is 0 characters)', 'is too short (minimum is 0 characters)',
......
...@@ -430,6 +430,26 @@ describe Projects::TreeController, "routing" do ...@@ -430,6 +430,26 @@ describe Projects::TreeController, "routing" do
end end
end end
describe Projects::EditTreeController, 'routing' do
it 'to #show' do
get('/gitlab/gitlabhq/edit/master/app/models/project.rb').should(
route_to('projects/edit_tree#show',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb'))
get('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should(
route_to('projects/edit_tree#show',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb/preview'))
end
it 'to #preview' do
post('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should(
route_to('projects/edit_tree#preview',
project_id: 'gitlab/gitlabhq',
id: 'master/app/models/project.rb'))
end
end
# project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/} # project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/}
# POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/} # POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/}
# project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/} # project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/}
......
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