Commit cf413ffb authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq into ce_6_6

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	LICENSE
	VERSION
	db/schema.rb
	features/steps/group/group.rb
	lib/api/internal.rb
parents 5771ff01 dad6b021
v 6.6.0 v 6.6.0
- Retrieving user ssh keys publically(github style): http://__HOST__/__USERNAME__.keys
- Permissions: Developer now can manage issue tracker (modify any issue) - Permissions: Developer now can manage issue tracker (modify any issue)
- Improve Code Compare page performance - Improve Code Compare page performance
- Group avatar - Group avatar
...@@ -12,6 +13,14 @@ v 6.6.0 ...@@ -12,6 +13,14 @@ v 6.6.0
- Mobile UI improvements (Drew Blessing) - Mobile UI improvements (Drew Blessing)
- Fix block/remove UI for admin::users#show page - Fix block/remove UI for admin::users#show page
- Show users' group membership on users' activity page - Show users' group membership on users' activity page
- User pages are visible without login if user is authorized to a public project
- Markdown rendered headers have id derived from their name and link to their id
- Improve application to work faster with large groups (100+ members)
- Multiple emails per user
- Show last commit for file when view file source
- Restyle Issue#show page and MR#show page
- Ability to filter by multiple labels for Issues page
- Rails version to 4.0.3
v 6.5.1 v 6.5.1
- Fix branch selectbox when create merge request from fork - Fix branch selectbox when create merge request from fork
......
...@@ -84,7 +84,7 @@ We will accept a merge requests if it: ...@@ -84,7 +84,7 @@ We will accept a merge requests if it:
* Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed) * Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed)
* Keeps the GitLab code base clean and well structured * Keeps the GitLab code base clean and well structured
* Contains functionality we think other users will benefit from too * Contains functionality we think other users will benefit from too
* Doesn't add avoidable configuration options since these complicate future changes * Doesn't add configuration options since these complicate future changes
* Contains a single commit (please use `git rebase -i` to squash commits) * Contains a single commit (please use `git rebase -i` to squash commits)
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed).
...@@ -29,7 +29,7 @@ gem 'omniauth-github' ...@@ -29,7 +29,7 @@ gem 'omniauth-github'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 5.3.0' gem "gitlab_git", '~> 5.4.0'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
...@@ -209,6 +209,10 @@ group :development, :test do ...@@ -209,6 +209,10 @@ group :development, :test do
gem 'spork', '~> 1.0rc' gem 'spork', '~> 1.0rc'
gem 'jasmine', '2.0.0.rc5' gem 'jasmine', '2.0.0.rc5'
gem "spring", '1.1.1'
gem "spring-commands-rspec", '1.0.1'
gem "spring-commands-spinach", '1.0.0'
end end
group :test do group :test do
......
...@@ -9,11 +9,11 @@ GEM ...@@ -9,11 +9,11 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
ace-rails-ap (2.0.1) ace-rails-ap (2.0.1)
actionmailer (4.0.2) actionmailer (4.0.3)
actionpack (= 4.0.2) actionpack (= 4.0.3)
mail (~> 2.5.4) mail (~> 2.5.4)
actionpack (4.0.2) actionpack (4.0.3)
activesupport (= 4.0.2) activesupport (= 4.0.3)
builder (~> 3.1.0) builder (~> 3.1.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rack (~> 1.5.2) rack (~> 1.5.2)
...@@ -22,16 +22,16 @@ GEM ...@@ -22,16 +22,16 @@ GEM
actionpack (>= 4.0.0, < 5.0) actionpack (>= 4.0.0, < 5.0)
actionpack-page_caching (1.0.2) actionpack-page_caching (1.0.2)
actionpack (>= 4.0.0, < 5) actionpack (>= 4.0.0, < 5)
activemodel (4.0.2) activemodel (4.0.3)
activesupport (= 4.0.2) activesupport (= 4.0.3)
builder (~> 3.1.0) builder (~> 3.1.0)
activerecord (4.0.2) activerecord (4.0.3)
activemodel (= 4.0.2) activemodel (= 4.0.3)
activerecord-deprecated_finders (~> 1.0.2) activerecord-deprecated_finders (~> 1.0.2)
activesupport (= 4.0.2) activesupport (= 4.0.3)
arel (~> 4.0.0) arel (~> 4.0.0)
activerecord-deprecated_finders (1.0.3) activerecord-deprecated_finders (1.0.3)
activesupport (4.0.2) activesupport (4.0.3)
i18n (~> 0.6, >= 0.6.4) i18n (~> 0.6, >= 0.6.4)
minitest (~> 4.2) minitest (~> 4.2)
multi_json (~> 1.3) multi_json (~> 1.3)
...@@ -43,7 +43,7 @@ GEM ...@@ -43,7 +43,7 @@ GEM
annotate (2.6.0) annotate (2.6.0)
activerecord (>= 2.3.0) activerecord (>= 2.3.0)
rake (>= 0.8.7) rake (>= 0.8.7)
arel (4.0.1) arel (4.0.2)
asciidoctor (0.1.4) asciidoctor (0.1.4)
atomic (1.1.14) atomic (1.1.14)
awesome_print (1.2.0) awesome_print (1.2.0)
...@@ -177,7 +177,7 @@ GEM ...@@ -177,7 +177,7 @@ GEM
charlock_holmes (~> 0.6.6) charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.4) escape_utils (~> 0.2.4)
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_git (5.3.0) gitlab_git (5.4.0)
activesupport (~> 4.0.0) activesupport (~> 4.0.0)
charlock_holmes (~> 0.6.9) charlock_holmes (~> 0.6.9)
gitlab-grit (~> 2.6.1) gitlab-grit (~> 2.6.1)
...@@ -320,7 +320,7 @@ GEM ...@@ -320,7 +320,7 @@ GEM
cliver (~> 0.2.1) cliver (~> 0.2.1)
multi_json (~> 1.0) multi_json (~> 1.0)
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
polyglot (0.3.3) polyglot (0.3.4)
posix-spawn (0.3.8) posix-spawn (0.3.8)
protected_attributes (1.0.5) protected_attributes (1.0.5)
activemodel (>= 4.0.1, < 5.0) activemodel (>= 4.0.1, < 5.0)
...@@ -346,13 +346,13 @@ GEM ...@@ -346,13 +346,13 @@ GEM
rack rack
rack-test (0.6.2) rack-test (0.6.2)
rack (>= 1.0) rack (>= 1.0)
rails (4.0.2) rails (4.0.3)
actionmailer (= 4.0.2) actionmailer (= 4.0.3)
actionpack (= 4.0.2) actionpack (= 4.0.3)
activerecord (= 4.0.2) activerecord (= 4.0.3)
activesupport (= 4.0.2) activesupport (= 4.0.3)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.0.2) railties (= 4.0.3)
sprockets-rails (~> 2.0.0) sprockets-rails (~> 2.0.0)
rails-observers (0.1.2) rails-observers (0.1.2)
activemodel (~> 4.0) activemodel (~> 4.0)
...@@ -365,13 +365,13 @@ GEM ...@@ -365,13 +365,13 @@ GEM
i18n i18n
require_all require_all
ruby-progressbar ruby-progressbar
railties (4.0.2) railties (4.0.3)
actionpack (= 4.0.2) actionpack (= 4.0.3)
activesupport (= 4.0.2) activesupport (= 4.0.3)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
raindrops (0.12.0) raindrops (0.12.0)
rake (10.1.0) rake (10.1.1)
raphael-rails (2.1.2) raphael-rails (2.1.2)
rb-fsevent (0.9.3) rb-fsevent (0.9.3)
rb-inotify (0.9.2) rb-inotify (0.9.2)
...@@ -470,6 +470,11 @@ GEM ...@@ -470,6 +470,11 @@ GEM
railties (>= 3) railties (>= 3)
spinach (>= 0.4) spinach (>= 0.4)
spork (1.0.0rc4) spork (1.0.0rc4)
spring (1.1.1)
spring-commands-rspec (1.0.1)
spring (>= 0.9.1)
spring-commands-spinach (1.0.0)
spring (>= 0.9.1)
sprockets (2.10.1) sprockets (2.10.1)
hike (~> 1.2) hike (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
...@@ -579,7 +584,7 @@ DEPENDENCIES ...@@ -579,7 +584,7 @@ DEPENDENCIES
gitlab-gollum-lib (~> 1.1.0) gitlab-gollum-lib (~> 1.1.0)
gitlab-grack (~> 2.0.0.pre) gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0) gitlab-linguist (~> 3.0.0)
gitlab_git (~> 5.3.0) gitlab_git (~> 5.4.0)
gitlab_meta (= 6.0) gitlab_meta (= 6.0)
gitlab_omniauth-ldap (= 1.0.4) gitlab_omniauth-ldap (= 1.0.4)
gon (~> 5.0.0) gon (~> 5.0.0)
...@@ -638,6 +643,9 @@ DEPENDENCIES ...@@ -638,6 +643,9 @@ DEPENDENCIES
slim slim
spinach-rails spinach-rails
spork (~> 1.0rc) spork (~> 1.0rc)
spring (= 1.1.1)
spring-commands-rspec (= 1.0.1)
spring-commands-spinach (= 1.0.0)
stamp stamp
state_machine state_machine
test_after_commit test_after_commit
......
...@@ -21,3 +21,5 @@ release where the minor version is increased numerically by increments of one ...@@ -21,3 +21,5 @@ release where the minor version is increased numerically by increments of one
(eg. `5.0 -> 5.1`). (eg. `5.0 -> 5.1`).
We encourage everyone to run the latest stable release to ensure that you can easily upgrade to the most secure and feature rich GitLab experience. In order to make sure you can easily run the most recent stable release, we are working hard to keep the update process simple and reliable. We encourage everyone to run the latest stable release to ensure that you can easily upgrade to the most secure and feature rich GitLab experience. In order to make sure you can easily run the most recent stable release, we are working hard to keep the update process simple and reliable.
More information about the release procedures can be found in the doc/release directory.
...@@ -15,12 +15,10 @@ ...@@ -15,12 +15,10 @@
### Code status ### Code status
* [![build status](http://ci.gitlab.org/projects/1/status.png?ref=master)](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) * [![build status](https://ci.gitlab.org/projects/1/status.png?ref=master)](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) * [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available), gems are updated in major releases of GitLab.
* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq) * [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
### Resources ### Resources
...@@ -33,6 +31,8 @@ ...@@ -33,6 +31,8 @@
* [GitLab CI](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab. * [GitLab CI](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab.
* Unofficial third-party [iPhone app](http://gitlabcontrol.com/) and [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en) for GitLab
### Requirements ### Requirements
* Ubuntu/Debian** * Ubuntu/Debian**
...@@ -47,13 +47,17 @@ ...@@ -47,13 +47,17 @@
#### Official installation methods #### Official installation methods
* [Manual installation guide for a production server](doc/install/installation.md) * [GitLab packages (beta)](https://www.gitlab.com/downloads/) These packages contain GitLab and all its depencies (PostgreSQL, Redis, Nginx, Unicorn, etc.). They are made with [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md) that also contains the installation instructions. These packages currently support a reduced selection of GitLab's normal features. For instance, it is not yet possible to create/restore application backups or to use HTTPS.
* [GitLab virtual machine images](https://www.gitlab.com/downloads/) contain an operating system and a preinstalled GitLab. They are made with [GitLab Packer](https://gitlab.com/gitlab-org/gitlab-packer/blob/master/README.md) that also contains the installation instructions.
* [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation on a virtual machine with Vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies. * [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation on a virtual machine with Vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies.
* [Manual installation guide](doc/install/installation.md) This guide to set up a production server offers detailed and complete step-by-step instructions.
#### Third party one-click installers #### Third party one-click installers
* [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/blog_posts/host-your-git-repositories-in-55-seconds-with-gitlab) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app such as GitLab. * [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/blog_posts/host-your-git-repositories-in-55-seconds-with-gitlab) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app such as GitLab. We recommend selecting a droplet with [1GB of memory](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md).
* [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.). * [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.).
...@@ -150,6 +154,8 @@ or start each component separately ...@@ -150,6 +154,8 @@ or start each component separately
* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview. * [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview.
* [Gitter chat room](https://gitter.im/gitlabhq/gitlabhq#) here you can ask questions when you need help.
### Getting in touch ### Getting in touch
......
6.6.0.pre-ee 6.6.0.beta1-ee
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
notes_path: "/api/:version/projects/:id/notes.json" notes_path: "/api/:version/projects/:id/notes.json"
ldap_groups_path: "/api/:version/ldap/groups.json" ldap_groups_path: "/api/:version/ldap/groups.json"
namespaces_path: "/api/:version/namespaces.json" namespaces_path: "/api/:version/namespaces.json"
project_users_path: "/api/:version/projects/:id/users.json"
# Get 20 (depends on api) recent notes # Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest # and sort the ascending from oldest to newest
...@@ -51,6 +52,23 @@ ...@@ -51,6 +52,23 @@
).done (users) -> ).done (users) ->
callback(users) callback(users)
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
url = Api.buildUrl(Api.project_users_path)
url = url.replace(':id', project_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
dataType: "json"
).done (users) ->
callback(users)
# Return namespaces list. Filtered by query # Return namespaces list. Filtered by query
namespaces: (query, callback) -> namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path) url = Api.buildUrl(Api.namespaces_path)
......
...@@ -19,6 +19,8 @@ class Dispatcher ...@@ -19,6 +19,8 @@ class Dispatcher
switch page switch page
when 'projects:issues:index' when 'projects:issues:index'
Issues.init() Issues.init()
when 'projects:issues:show'
new Issue()
when 'projects:issues:new', 'projects:merge_requests:new' when 'projects:issues:new', 'projects:merge_requests:new'
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
when 'dashboard:show' when 'dashboard:show'
......
class Issue
constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
@Issue = Issue
...@@ -77,9 +77,3 @@ ...@@ -77,9 +77,3 @@
$("#update_issues_ids").val [] $("#update_issues_ids").val []
$(".issues_bulk_update").hide() $(".issues_bulk_update").hide()
$(".issues-filters").show() $(".issues-filters").show()
$ ->
$('.edit-issue.inline-update input[type="submit"]').hide();
$("body").on "change", ".edit-issue.inline-update select", ->
$(this).submit()
class MergeRequest
constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request')
@diffs_loaded = if @opts.action == 'diffs' then true else false
@commits_loaded = false
this.activateTab(@opts.action)
this.bindEvents()
this.initMergeWidget()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
modal = $('#modal_merge_info').modal(show: false)
disableButtonIfEmptyField '#merge_commit_message', '.accept_merge_request'
# Local jQuery finder
$: (selector) ->
this.$el.find(selector)
initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", ->
$(this).submit()
$(".issue-box .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
initMergeWidget: ->
this.showState( @opts.current_status )
if this.$('.automerge_widget').length and @opts.check_enable
$.get @opts.url_to_automerge_check, (data) =>
this.showState( data.merge_status )
, 'json'
if @opts.ci_enable
$.get @opts.url_to_ci_check, (data) =>
this.showCiState data.status
, 'json'
bindEvents: ->
this.$('.nav-tabs').on 'click', 'a', (event) =>
a = $(event.currentTarget)
href = a.attr('href')
History.replaceState {path: href}, document.title, href
event.preventDefault()
this.$('.nav-tabs').on 'click', 'li', (event) =>
this.activateTab($(event.currentTarget).data('action'))
this.$('.accept_merge_request').on 'click', ->
$('.automerge_widget.can_be_merged').hide()
$('.merge-in-progress').show()
activateTab: (action) ->
this.$('.nav-tabs li').removeClass 'active'
this.$('.tab-content').hide()
switch action
when 'diffs'
this.$('.nav-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
else
this.$('.nav-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
showState: (state) ->
$('.automerge_widget').hide()
$('.automerge_widget.' + state).show()
showCiState: (state) ->
$('.ci_widget').hide()
$('.ci_widget.ci-' + state).show()
loadDiff: (event) ->
$.ajax
type: 'GET'
url: this.$('.nav-tabs .diffs-tab a').attr('href')
beforeSend: =>
this.$('.status').addClass 'loading'
complete: =>
@diffs_loaded = true
this.$('.status').removeClass 'loading'
success: (data) =>
this.$(".diffs").html(data.html)
dataType: 'json'
showAllCommits: ->
this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide'
alreadyOrCannotBeMerged: ->
this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
this.MergeRequest = MergeRequest
...@@ -6,99 +6,3 @@ ...@@ -6,99 +6,3 @@
$('#milestone_id').select2() $('#milestone_id').select2()
$('#milestone_id, #assignee_id').on 'change', -> $('#milestone_id, #assignee_id').on 'change', ->
$(this).closest('form').submit() $(this).closest('form').submit()
class MergeRequest
constructor: (@opts) ->
this.$el = $('.merge-request')
@diffs_loaded = if @opts.action == 'diffs' then true else false
@commits_loaded = false
this.activateTab(@opts.action)
this.bindEvents()
this.initMergeWidget()
this.$('.show-all-commits').on 'click', =>
this.showAllCommits()
modal = $('#modal_merge_info').modal(show: false)
disableButtonIfEmptyField '#merge_commit_message', '.accept_merge_request'
# Local jQuery finder
$: (selector) ->
this.$el.find(selector)
initMergeWidget: ->
this.showState( @opts.current_status )
if this.$('.automerge_widget').length and @opts.check_enable
$.get @opts.url_to_automerge_check, (data) =>
this.showState( data.merge_status )
, 'json'
if @opts.ci_enable
$.get @opts.url_to_ci_check, (data) =>
this.showCiState data.status
, 'json'
bindEvents: ->
this.$('.nav-tabs').on 'click', 'a', (event) =>
a = $(event.currentTarget)
href = a.attr('href')
History.replaceState {path: href}, document.title, href
event.preventDefault()
this.$('.nav-tabs').on 'click', 'li', (event) =>
this.activateTab($(event.currentTarget).data('action'))
this.$('.accept_merge_request').on 'click', ->
$('.automerge_widget.can_be_merged').hide()
$('.merge-in-progress').show()
activateTab: (action) ->
this.$('.nav-tabs li').removeClass 'active'
this.$('.tab-content').hide()
switch action
when 'diffs'
this.$('.nav-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
else
this.$('.nav-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
showState: (state) ->
$('.automerge_widget').hide()
$('.automerge_widget.' + state).show()
showCiState: (state) ->
$('.ci_widget').hide()
$('.ci_widget.ci-' + state).show()
loadDiff: (event) ->
$.ajax
type: 'GET'
url: this.$('.nav-tabs .diffs-tab a').attr('href')
beforeSend: =>
this.$('.status').addClass 'loading'
complete: =>
@diffs_loaded = true
this.$('.status').removeClass 'loading'
success: (data) =>
this.$(".diffs").html(data.html)
dataType: 'json'
showAllCommits: ->
this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide'
alreadyOrCannotBeMerged: ->
this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
this.MergeRequest = MergeRequest
@projectUsersSelect =
init: ->
$('.ajax-project-users-select').each (i, select) ->
project_id = $('body').data('project-id')
$(select).select2
placeholder: $(select).data('placeholder') || "Search for a user"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.projectUsers project_id, query.term, (users) ->
data = { results: users }
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id isnt ""
Api.user(id, callback)
formatResult: projectUsersSelect.projectUserFormatResult
formatSelection: projectUsersSelect.projectUserFormatSelection
dropdownCssClass: "ajax-project-users-dropdown"
dropdownAutoWidth: true
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
projectUserFormatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else if gon.gravatar_enabled
avatar = gon.gravatar_url
avatar = avatar.replace('%{hash}', md5(user.email))
avatar = avatar.replace('%{size}', '24')
else
avatar = gon.relative_url_root + "/assets/no_avatar.png"
"<div class='user-result'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
projectUserFormatSelection: (user) ->
user.name
$ ->
projectUsersSelect.init()
$ -> $ ->
userFormatResult = (user) -> userFormatResult = (user) ->
if user.avatar if user.avatar_url
avatar = user.avatar.url avatar = user.avatar_url
else else if gon.gravatar_enabled
avatar = gon.gravatar_url avatar = gon.gravatar_url
avatar = avatar.replace('%{hash}', md5(user.email)) avatar = avatar.replace('%{hash}', md5(user.email))
avatar = avatar.replace('%{size}', '24') avatar = avatar.replace('%{size}', '24')
else
avatar = gon.relative_url_root + "/assets/no_avatar.png"
"<div class='user-result'> "<div class='user-result'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div> <div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
@import "sections/wall.scss"; @import "sections/wall.scss";
@import "sections/dashboard.scss"; @import "sections/dashboard.scss";
@import "sections/stat_graph.scss"; @import "sections/stat_graph.scss";
@import "sections/groups.scss";
/** /**
* Code ighlight * Code ighlight
......
...@@ -118,7 +118,6 @@ ...@@ -118,7 +118,6 @@
@extend .btn-primary; @extend .btn-primary;
} }
&.btn-close,
&.btn-remove { &.btn-remove {
@extend .btn-danger; @extend .btn-danger;
} }
...@@ -143,6 +142,22 @@ ...@@ -143,6 +142,22 @@
line-height: 16px; line-height: 16px;
margin: 2px; margin: 2px;
} }
&.btn-close {
color: #B94A48;
font-weight: bold;
&:hover {
color: #B94A48;
}
}
&.btn-reopen {
color: #468847;
font-weight: bold;
&:hover {
color: #468847;
}
}
} }
.btn-block { .btn-block {
......
...@@ -88,11 +88,15 @@ pre.well-pre { ...@@ -88,11 +88,15 @@ pre.well-pre {
/** Big Labels **/ /** Big Labels **/
.state-label { .state-label {
font-size: 14px; font-size: 14px;
padding: 6px 25px; padding: 9px 25px;
text-align: center; text-align: center;
@include border-radius(4px);
text-shadow: none; text-shadow: none;
margin-left: 10px; margin-right: 20px;
&.state-label-blue {
background: #31708f;
color: #FFF;
}
&.state-label-green { &.state-label-green {
background: #4A4; background: #4A4;
...@@ -112,6 +116,7 @@ pre.well-pre { ...@@ -112,6 +116,7 @@ pre.well-pre {
.dropdown-menu > li > a:hover, .dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus { .dropdown-menu > li > a:focus {
background: #29b; background: #29b;
color: #FFF
} }
.breadcrumb > li + li:before { .breadcrumb > li + li:before {
......
...@@ -50,9 +50,9 @@ ...@@ -50,9 +50,9 @@
} }
&.wiki { &.wiki {
padding: 20px;
font-size: 14px; font-size: 14px;
line-height: 1.6; line-height: 1.6;
padding: 25px;
.highlight { .highlight {
margin-bottom: 9px; margin-bottom: 9px;
......
...@@ -51,3 +51,27 @@ label { ...@@ -51,3 +51,27 @@ label {
.input-mn-300 { .input-mn-300 {
min-width: 300px; min-width: 300px;
} }
.custom-form-control {
width: 150px;
}
@media (min-width: $screen-sm-min) {
.custom-form-control {
width: 150px;
}
}
/* Medium devices (desktops, 992px and up) */
@media (min-width: $screen-md-min) {
.custom-form-control {
width: 170px;
}
}
/* Large devices (large desktops, 1200px and up) */
@media (min-width: $screen-lg-min) {
.custom-form-control {
width: 200px;
}
}
...@@ -17,26 +17,33 @@ ...@@ -17,26 +17,33 @@
margin-bottom: 0; margin-bottom: 0;
} }
.state {
height: 34px;
border-bottom: 1px solid #DDD;
line-height: 32px;
}
.title { .title {
font-size: 20px; font-size: 22px;
font-weight: 500; font-weight: 500;
line-height: 28px; line-height: 1.5;
margin: 0; margin: 0;
color: #444; color: #333;
padding-bottom: 0;
padding: 15px 25px;
} }
.context { .context {
border: none; border: none;
border-top: 1px solid #eee; border-top: 1px solid #eee;
padding: 15px 25px;
} }
.description { .description {
border-top: 1px solid #eee; padding: 0 25px 15px 25px;
} }
.title, .context, .description { .title, .context, .description {
padding: 15px;
.clearfix { .clearfix {
margin: 0; margin: 0;
} }
......
/** Select2 selectbox style override **/ /** Select2 selectbox style override **/
.select2-container, .select2-container.select2-drop-above { .select2-container, .select2-container.select2-drop-above {
.select2-choice { .select2-choice {
background: #FFF; background: #FFF;
...@@ -12,9 +11,13 @@ ...@@ -12,9 +11,13 @@
} }
.select2-drop-active { .select2-drop-active {
border: 1px solid #BBB; border: 1px solid #BBB !important;
margin-top: 4px; margin-top: 4px;
&.select2-drop-above {
margin-bottom: 8px;
}
.select2-search input { .select2-search input {
background: #fafafa; background: #fafafa;
border-color: #DDD; border-color: #DDD;
...@@ -78,3 +81,9 @@ select { ...@@ -78,3 +81,9 @@ select {
.project-refs-form .select2-container { .project-refs-form .select2-container {
margin-right: 10px; margin-right: 10px;
} }
.ajax-users-dropdown, .ajax-project-users-dropdown {
.select2-search {
padding-top: 4px;
}
}
...@@ -90,6 +90,27 @@ a:focus { ...@@ -90,6 +90,27 @@ a:focus {
font-size: 14px; font-size: 14px;
line-height: 1.6; line-height: 1.6;
/* Link to current header. */
h1, h2, h3, h4, h5, h6 {
position: relative;
&:hover > :last-child {
$size: 16px;
position: absolute;
right: 100%;
top: 50%;
margin-top: -$size/2;
margin-right: 0px;
padding-right: 20px;
display: inline-block;
width: $size;
height: $size;
background-image: url("icon-link.png");
background-size: contain;
background-repeat: no-repeat;
}
}
ul { ul {
padding: 0; padding: 0;
margin: 0 0 9px 25px !important; margin: 0 0 9px 25px !important;
......
...@@ -108,6 +108,8 @@ $pagination-active-bg: $bg_style_color; ...@@ -108,6 +108,8 @@ $pagination-active-bg: $bg_style_color;
// Nav tabs // Nav tabs
.nav.nav-tabs { .nav.nav-tabs {
margin-bottom: 15px;
li { li {
> a { > a {
padding: 8px 20px; padding: 8px 20px;
......
...@@ -5,6 +5,7 @@ $primary_color: #2FA0BB; ...@@ -5,6 +5,7 @@ $primary_color: #2FA0BB;
$link_color: #3A89A3; $link_color: #3A89A3;
$style_color: #474D57; $style_color: #474D57;
$bg_style_color: #2299BB; $bg_style_color: #2299BB;
$list-group-active-bg: $bg_style_color;
$hover: #D9EDF7; $hover: #D9EDF7;
/** /**
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Admin area * Admin area
* *
*/ */
.admin_dash { .admin-dashboard {
.data { .data {
a { a {
h1 { h1 {
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
} }
} }
} }
.str-truncated {
max-width: 60%;
}
} }
.admin-filter form { .admin-filter form {
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
.dash-sidebar-tabs { .dash-sidebar-tabs {
margin-bottom: 2px; margin-bottom: 2px;
border: none; border: none;
margin: 0; margin: 0 !important;
li { li {
&.active { &.active {
......
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
&.event-inline { &.event-inline {
.avatar { .avatar {
width: 16px; position: relative;
height: 16px; top: -2px;
} }
} }
......
.new-group-member-holder {
margin-top: 50px;
background: #f9f9f9;
padding-top: 20px;
}
.member-search-form {
float: left;
}
...@@ -56,7 +56,7 @@ header { ...@@ -56,7 +56,7 @@ header {
font-size: 18px; font-size: 18px;
.app_logo { margin-left: -15px; } .app_logo { margin-left: -15px; }
.project_name { .title {
display: inline-block; display: inline-block;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
...@@ -108,7 +108,7 @@ header { ...@@ -108,7 +108,7 @@ header {
h1 { h1 {
margin: 0; margin: 0;
background: url('logo-black.png') no-repeat center center; background: image-url('logo-black.png') no-repeat center center;
background-size: 32px; background-size: 32px;
float: left; float: left;
height: 46px; height: 46px;
...@@ -127,7 +127,7 @@ header { ...@@ -127,7 +127,7 @@ header {
* Project / Area name * Project / Area name
* *
*/ */
.project_name { .title {
position: relative; position: relative;
float: left; float: left;
margin: 0; margin: 0;
...@@ -220,18 +220,18 @@ header { ...@@ -220,18 +220,18 @@ header {
.app_logo { .app_logo {
a { a {
h1 { h1 {
background: url('logo-white.png') no-repeat center center; background: image-url('logo-white.png') no-repeat center center;
background-size: 32px; background-size: 32px;
color: #fff; color: #fff;
text-shadow: 0 1px 1px #444; text-shadow: 0 1px 1px #444;
} }
} }
} }
.project_name { .title {
a { a {
color: #BBB;
&:hover {
color: #FFF; color: #FFF;
&:hover {
text-decoration: underline;
} }
} }
color: #fff; color: #fff;
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
.issue-check { .issue-check {
float: left; float: left;
padding: 8px 0;
padding-right: 8px; padding-right: 8px;
margin-bottom: 10px;
min-width: 15px; min-width: 15px;
} }
...@@ -38,13 +38,21 @@ ...@@ -38,13 +38,21 @@
} }
} }
input.check_all_issues { .check-all-holder {
height: 32px;
float: left; float: left;
margin-right: 12px;
padding: 6px 10px;
border: 1px solid #ccc;
@include border-radius(4px);
input.check_all_issues {
padding: 0; padding: 0;
margin: 0; margin: 0;
margin-right: 10px;
position: relative; position: relative;
top: 13px; top: 3px;
}
} }
.issues_content { .issues_content {
...@@ -57,21 +65,6 @@ input.check_all_issues { ...@@ -57,21 +65,6 @@ input.check_all_issues {
} }
} }
.btn.close_issue {
color: #B94A48;
font-weight: bold;
&:hover {
color: #B94A48;
}
}
.btn.reopen_issue {
color: #468847;
font-weight: bold;
&:hover {
color: #468847;
}
}
@media (min-width: 800px) { .issues_filters select { width: 160px; } } @media (min-width: 800px) { .issues_filters select { width: 160px; } }
@media (min-width: 1200px) { .issues_filters select { width: 220px; } } @media (min-width: 1200px) { .issues_filters select { width: 220px; } }
...@@ -91,6 +84,13 @@ input.check_all_issues { ...@@ -91,6 +84,13 @@ input.check_all_issues {
.update_selected_issues { .update_selected_issues {
margin-left: 4px; margin-left: 4px;
} }
.select2-container .select2-choice {
height: 32px;
line-height: 28px;
color: #444 !important;
font-weight: 500;
}
} }
} }
......
...@@ -31,10 +31,10 @@ ...@@ -31,10 +31,10 @@
.mr_source_commit, .mr_source_commit,
.mr_target_commit { .mr_target_commit {
margin-top: 10px;
.commit { .commit {
margin: 0; margin: 0;
padding: 0; padding: 2px 0;
padding: 5px 0;
list-style: none; list-style: none;
&:hover { &:hover {
background: none; background: none;
......
...@@ -36,8 +36,7 @@ ...@@ -36,8 +36,7 @@
&.active { &.active {
a { a {
color: #333; color: #333;
font-weight: bolder; font-weight: bold;
&:after { &:after {
content: ''; content: '';
display: block; display: block;
...@@ -56,7 +55,20 @@ ...@@ -56,7 +55,20 @@
&:hover { &:hover {
a { a {
color: $style_color; color: $link_color;
&:after {
content: '';
display: block;
position: relative;
bottom: 8px;
left: 50%;
width: 0;
height: 0;
border-color: transparent transparent #29b transparent;
border-style: solid;
border-width: 6px;
margin-left: -6px;
}
} }
} }
...@@ -73,7 +85,7 @@ ...@@ -73,7 +85,7 @@
a { a {
display: block; display: block;
text-align: center; text-align: center;
font-weight: normal; font-weight: 500;
height: 38px; height: 38px;
line-height: 34px; line-height: 34px;
color: #777; color: #777;
......
...@@ -114,3 +114,14 @@ ...@@ -114,3 +114,14 @@
height: 50px; height: 50px;
} }
} }
.global-notifications-form .level-title {
font-size: 15px;
color: #333;
font-weight: bold;
}
.notification-icon-holder {
width: 20px;
float: left;
}
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
border-top: 1px dashed #CCC; border-top: 1px dashed #CCC;
padding-top: 10px; padding-top: 10px;
h4 { .readme-file-title {
font-size: 14px; font-size: 14px;
margin-bottom: 20px; margin-bottom: 20px;
color: #777; color: #777;
......
...@@ -36,4 +36,8 @@ ...@@ -36,4 +36,8 @@
} }
} }
} }
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
background: #769;
}
} }
...@@ -136,12 +136,12 @@ class ApplicationController < ActionController::Base ...@@ -136,12 +136,12 @@ class ApplicationController < ActionController::Base
end end
end end
def render_404 def render_403
render file: Rails.root.join("public", "404"), layout: false, status: "404" head :forbidden
end end
def render_403 def render_404
render file: Rails.root.join("public", "403"), layout: false, status: "403" render file: Rails.root.join("public", "404"), layout: false, status: "404"
end end
def require_non_empty_project def require_non_empty_project
...@@ -172,6 +172,7 @@ class ApplicationController < ActionController::Base ...@@ -172,6 +172,7 @@ class ApplicationController < ActionController::Base
gon.api_token = current_user.private_token if current_user gon.api_token = current_user.private_token if current_user
gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.gravatar_enabled = Gitlab.config.gravatar.enabled
end end
def check_password_expiration def check_password_expiration
......
...@@ -65,7 +65,14 @@ class GroupsController < ApplicationController ...@@ -65,7 +65,14 @@ class GroupsController < ApplicationController
def members def members
@project = group.projects.find(params[:project_id]) if params[:project_id] @project = group.projects.find(params[:project_id]) if params[:project_id]
@members = group.users_groups.order('group_access DESC') @members = group.users_groups
if params[:search].present?
users = group.users.search(params[:search])
@members = @members.where(user_id: users)
end
@members = @members.order('group_access DESC').page(params[:page]).per(50)
@users_group = UsersGroup.new @users_group = UsersGroup.new
end end
......
class Profiles::EmailsController < ApplicationController
layout "profile"
def index
@primary = current_user.email
@emails = current_user.emails
end
def create
@email = current_user.emails.new(params[:email])
flash[:alert] = @email.errors.full_messages.first unless @email.save
redirect_to profile_emails_url
end
def destroy
@email = current_user.emails.find(params[:id])
@email.destroy
respond_to do |format|
format.html { redirect_to profile_emails_url }
format.js { render nothing: true }
end
end
end
...@@ -7,12 +7,11 @@ class Profiles::GroupsController < ApplicationController ...@@ -7,12 +7,11 @@ class Profiles::GroupsController < ApplicationController
def leave def leave
@users_group = group.users_groups.where(user_id: current_user.id).first @users_group = group.users_groups.where(user_id: current_user.id).first
if can?(current_user, :destroy, @users_group)
if group.last_owner?(current_user)
redirect_to(profile_groups_path, alert: "You can't leave group. You must add at least one more owner to it.")
else
@users_group.destroy @users_group.destroy
redirect_to(profile_groups_path, info: "You left #{group.name} group.") redirect_to(profile_groups_path, info: "You left #{group.name} group.")
else
return render_403
end end
end end
......
class Profiles::KeysController < ApplicationController class Profiles::KeysController < ApplicationController
layout "profile" layout "profile"
skip_before_filter :authenticate_user!, only: [:get_keys]
def index def index
@keys = current_user.keys.order('id DESC') @keys = current_user.keys.order('id DESC')
...@@ -32,4 +33,24 @@ class Profiles::KeysController < ApplicationController ...@@ -32,4 +33,24 @@ class Profiles::KeysController < ApplicationController
format.js { render nothing: true } format.js { render nothing: true }
end end
end end
# Get all keys of a user(params[:username]) in a text format
# Helpful for sysadmins to put in respective servers
def get_keys
if params[:username].present?
begin
user = User.find_by_username(params[:username])
if user.present?
render text: user.all_ssh_keys.join("\n")
else
render_404 and return
end
rescue => e
render text: e.message
end
else
render_404 and return
end
end
end end
...@@ -15,11 +15,7 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -15,11 +15,7 @@ class Projects::CompareController < Projects::ApplicationController
@diffs = compare.diffs @diffs = compare.diffs
@refs_are_same = compare.same @refs_are_same = compare.same
@line_notes = [] @line_notes = []
@timeout = compare.timeout
if @diffs == [Gitlab::Git::Diff::BROKEN_DIFF]
@diffs = []
@timeout = true
end
diff_line_count = Commit::diff_line_count(@diffs) diff_line_count = Commit::diff_line_count(@diffs)
@suppress_diff = Commit::diff_suppress?(@diffs, diff_line_count) && !params[:force_show_diff] @suppress_diff = Commit::diff_suppress?(@diffs, diff_line_count) && !params[:force_show_diff]
......
...@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
sort_param = params[:sort] || 'newest' sort_param = params[:sort] || 'newest'
@sort = sort_param.humanize unless sort_param.empty? @sort = sort_param.humanize unless sort_param.empty?
@assignees = User.where(id: @project.issues.pluck(:assignee_id))
respond_to do |format| respond_to do |format|
format.html format.html
......
...@@ -28,6 +28,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -28,6 +28,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
@assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
@assignees = User.where(id: @project.merge_requests.pluck(:assignee_id))
end end
def show def show
...@@ -105,10 +106,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -105,10 +106,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_request].delete(:target_project_id) params[:merge_request].delete(:target_project_id)
if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id)) if @merge_request.update_attributes(params[:merge_request].merge(author_id_of_changes: current_user.id))
@merge_request.reload_code
@merge_request.mark_as_unchecked
@merge_request.reset_events_cache @merge_request.reset_events_cache
respond_to do |format|
format.js
format.html do
redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.' redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.'
end
end
else else
render "edit" render "edit"
end end
......
...@@ -11,11 +11,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -11,11 +11,7 @@ class Projects::RawController < Projects::ApplicationController
@blob = @repository.blob_at(@commit.id, @path) @blob = @repository.blob_at(@commit.id, @path)
if @blob if @blob
type = if @blob.mime_type =~ /html|javascript/ type = get_blob_type
'text/plain; charset=utf-8'
else
@blob.mime_type
end
headers['X-Content-Type-Options'] = 'nosniff' headers['X-Content-Type-Options'] = 'nosniff'
...@@ -29,5 +25,17 @@ class Projects::RawController < Projects::ApplicationController ...@@ -29,5 +25,17 @@ class Projects::RawController < Projects::ApplicationController
not_found! not_found!
end end
end end
private
def get_blob_type
if @blob.mime_type =~ /html|javascript/
'text/plain; charset=utf-8'
elsif @blob.name =~ /(?:msi|exe|rar|r0\d|7z|7zip|zip)$/
'application/octet-stream'
else
@blob.mime_type
end
end
end end
class UsersController < ApplicationController class UsersController < ApplicationController
layout 'navless'
skip_before_filter :authenticate_user!, only: [:show]
layout :determine_layout
def show def show
@user = User.find_by!(username: params[:username]) @user = User.find_by_username!(params[:username])
@projects = @user.authorized_projects.where(id: current_user.authorized_projects.pluck(:id)).includes(:namespace) @projects = @user.authorized_projects.includes(:namespace).select {|project| can?(current_user, :read_project, project)}
if !current_user && @projects.empty?
return authenticate_user!
end
@events = @user.recent_events.where(project_id: @projects.map(&:id)).limit(20) @events = @user.recent_events.where(project_id: @projects.map(&:id)).limit(20)
@title = @user.name @title = @user.name
end end
def determine_layout
if current_user
'navless'
else
'public_users'
end
end
end end
...@@ -19,12 +19,15 @@ class UsersGroupsController < ApplicationController ...@@ -19,12 +19,15 @@ class UsersGroupsController < ApplicationController
def destroy def destroy
@users_group = @group.users_groups.find(params[:id]) @users_group = @group.users_groups.find(params[:id])
if can?(current_user, :destroy, @users_group) # May fail if last owner.
@users_group.destroy @users_group.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' } format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' }
format.js { render nothing: true } format.js { render nothing: true }
end end
else
return render_403
end
end end
protected protected
......
...@@ -162,15 +162,6 @@ module ApplicationHelper ...@@ -162,15 +162,6 @@ module ApplicationHelper
alias_method :url_to_image, :image_url alias_method :url_to_image, :image_url
def users_select_tag(id, opts = {})
css_class = "ajax-users-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
def body_data_page def body_data_page
path = controller.controller_path.split('/') path = controller.controller_path.split('/')
namespace = path.first if path.second namespace = path.first if path.second
......
...@@ -122,17 +122,18 @@ module CommitsHelper ...@@ -122,17 +122,18 @@ module CommitsHelper
def commit_person_link(commit, options = {}) def commit_person_link(commit, options = {})
source_name = commit.send "#{options[:source]}_name".to_sym source_name = commit.send "#{options[:source]}_name".to_sym
source_email = commit.send "#{options[:source]}_email".to_sym source_email = commit.send "#{options[:source]}_email".to_sym
user = User.find_for_commit(source_email, source_name)
person_name = user.nil? ? source_name : user.name
person_email = user.nil? ? source_email : user.email
text = if options[:avatar] text = if options[:avatar]
avatar = image_tag(avatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>} %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
else else
source_name person_name
end end
# Prefer email match over name match
user = User.where(email: source_email).first
user ||= User.where(name: source_name).first
options = { options = {
class: "commit-#{options[:source]}-link has_tooltip", class: "commit-#{options[:source]}-link has_tooltip",
data: { :'original-title' => sanitize(source_email) } data: { :'original-title' => sanitize(source_email) }
......
...@@ -28,14 +28,16 @@ module GitlabMarkdownHelper ...@@ -28,14 +28,16 @@ module GitlabMarkdownHelper
link_to(gfm_body.html_safe, url, html_options) link_to(gfm_body.html_safe, url, html_options)
end end
def markdown(text) def markdown(text, options={})
unless @markdown unless (@markdown and options == @options)
gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, @options = options
gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, {
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true, filter_html: true,
with_toc_data: true, with_toc_data: true,
hard_wrap: true, hard_wrap: true,
safe_links_only: true) safe_links_only: true
}.merge(options))
@markdown = Redcarpet::Markdown.new(gitlab_renderer, @markdown = Redcarpet::Markdown.new(gitlab_renderer,
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
no_intra_emphasis: true, no_intra_emphasis: true,
...@@ -47,7 +49,6 @@ module GitlabMarkdownHelper ...@@ -47,7 +49,6 @@ module GitlabMarkdownHelper
space_after_headers: true, space_after_headers: true,
superscript: true) superscript: true)
end end
@markdown.render(text).html_safe @markdown.render(text).html_safe
end end
......
module GroupsHelper module GroupsHelper
def remove_user_from_group_message(group, user) def remove_user_from_group_message(group, user)
"You are going to remove #{user.name} from #{group.name} Group. Are you sure?" "Are you sure you want to remove \"#{user.name}\" from \"#{group.name}\"?"
end
def leave_group_message(group)
"Are you sure you want to leave \"#{group}\" group?"
end end
def group_head_title def group_head_title
......
...@@ -70,11 +70,11 @@ module IssuesHelper ...@@ -70,11 +70,11 @@ module IssuesHelper
end end
def bulk_update_milestone_options def bulk_update_milestone_options
options_for_select(["None (backlog)", nil]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]) options_for_select(["None (backlog)"]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id])
end end
def bulk_update_assignee_options def bulk_update_assignee_options
options_for_select(["None (unassigned)", nil]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]) options_for_select(["None (unassigned)"]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id])
end end
def assignee_options object def assignee_options object
...@@ -84,4 +84,12 @@ module IssuesHelper ...@@ -84,4 +84,12 @@ module IssuesHelper
def milestone_options object def milestone_options object
options_from_collection_for_select(@project.milestones.active, 'id', 'title', object.milestone_id) options_from_collection_for_select(@project.milestones.active, 'id', 'title', object.milestone_id)
end end
def issue_alert_class(issue)
if issue.closed?
'alert-danger'
else
'alert-success'
end
end
end end
...@@ -41,4 +41,14 @@ module MergeRequestsHelper ...@@ -41,4 +41,14 @@ module MergeRequestsHelper
"Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
end end
end end
def merge_request_alert_class(merge_request)
if merge_request.merged?
'alert-info'
elsif merge_request.closed?
'alert-danger'
else
'alert-success'
end
end
end end
...@@ -17,7 +17,7 @@ module NotesHelper ...@@ -17,7 +17,7 @@ module NotesHelper
def link_to_merge_request_diff_line_note(note) def link_to_merge_request_diff_line_note(note)
if note.for_merge_request_diff_line? and note.diff if note.for_merge_request_diff_line? and note.diff
link_to "#{note.diff_file_name}:L#{note.diff_new_line}", diffs_project_merge_request_path(note.project, note.noteable_id, anchor: note.line_code) link_to "#{note.diff_file_name}:L#{note.diff_new_line}", diffs_project_merge_request_path(note.project, note.noteable, anchor: note.line_code)
end end
end end
......
module NotificationsHelper module NotificationsHelper
def notification_icon(notification) def notification_icon(notification)
if notification.disabled? if notification.disabled?
content_tag :i, nil, class: 'icon-circle cred' content_tag :i, nil, class: 'icon-volume-off cred'
elsif notification.participating? elsif notification.participating?
content_tag :i, nil, class: 'icon-circle cblue' content_tag :i, nil, class: 'icon-volume-down cblue'
elsif notification.watch? elsif notification.watch?
content_tag :i, nil, class: 'icon-circle cgreen' content_tag :i, nil, class: 'icon-volume-up cgreen'
else else
content_tag :i, nil, class: 'icon-circle-blank cblue' content_tag :i, nil, class: 'icon-circle-blank cblue'
end end
......
...@@ -64,6 +64,31 @@ module ProjectsHelper ...@@ -64,6 +64,31 @@ 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={}) def project_filter_path(options={})
exist_opts = { exist_opts = {
state: params[:state], state: params[:state],
......
module SelectsHelper
def users_select_tag(id, opts = {})
css_class = "ajax-users-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
def project_users_select_tag(id, opts = {})
css_class = "ajax-project-users-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Select user'
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder)
end
end
...@@ -6,6 +6,12 @@ module Emails ...@@ -6,6 +6,12 @@ module Emails
mail(to: @user.email, subject: subject("Account was created for you")) mail(to: @user.email, subject: subject("Account was created for you"))
end end
def new_email_email(email_id)
@email = Email.find(email_id)
@user = @email.user
mail(to: @user.email, subject: subject("Email was added to your account"))
end
def new_ssh_key_email(key_id) def new_ssh_key_email(key_id)
@key = Key.find(key_id) @key = Key.find(key_id)
@user = @key.user @user = @key.user
......
...@@ -17,6 +17,7 @@ module Emails ...@@ -17,6 +17,7 @@ module Emails
def repository_push_email(project_id, recipient, author_id, branch, compare) def repository_push_email(project_id, recipient, author_id, branch, compare)
@project = Project.find(project_id) @project = Project.find(project_id)
@author = User.find(author_id) @author = User.find(author_id)
@compare = compare
@commits = Commit.decorate(compare.commits) @commits = Commit.decorate(compare.commits)
@diffs = compare.diffs @diffs = compare.diffs
@branch = branch @branch = branch
......
...@@ -14,6 +14,7 @@ class Ability ...@@ -14,6 +14,7 @@ class Ability
when "MergeRequest" then merge_request_abilities(user, subject) when "MergeRequest" then merge_request_abilities(user, subject)
when "Group" then group_abilities(user, subject) when "Group" then group_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject) when "Namespace" then namespace_abilities(user, subject)
when "UsersGroup" then users_group_abilities(user, subject)
else [] else []
end.concat(global_abilities(user)) end.concat(global_abilities(user))
end end
...@@ -219,5 +220,19 @@ class Ability ...@@ -219,5 +220,19 @@ class Ability
end end
end end
end end
def users_group_abilities(user, subject)
rules = []
target_user = subject.user
group = subject.group
can_manage = group_abilities(user, group).include?(:manage_group)
if can_manage && (user != target_user)
rules << :modify
end
if !group.last_owner?(user) && (can_manage || (user == target_user))
rules << :destroy
end
rules
end
end end
end end
# == Schema Information
#
# Table name: emails
#
# id :integer not null, primary key
# user_id :integer not null
# email :string not null
# created_at :datetime not null
class Email < ActiveRecord::Base
attr_accessible :email, :user_id
#
# Relations
#
belongs_to :user
#
# Validations
#
validates :user_id, presence: true
validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
validate :unique_email, if: ->(email) { email.email_changed? }
before_validation :cleanup_email
def cleanup_email
self.email = self.email.downcase.strip
end
def unique_email
self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email)
end
end
\ No newline at end of file
...@@ -32,7 +32,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -32,7 +32,9 @@ class MergeRequest < ActiveRecord::Base
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
has_one :merge_request_diff, dependent: :destroy has_one :merge_request_diff, dependent: :destroy
after_create :create_merge_request_diff after_create :create_merge_request_diff
after_update :update_merge_request_diff
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
...@@ -125,6 +127,13 @@ class MergeRequest < ActiveRecord::Base ...@@ -125,6 +127,13 @@ class MergeRequest < ActiveRecord::Base
end end
end end
def update_merge_request_diff
if source_branch_changed? || target_branch_changed?
reload_code
mark_as_unchecked
end
end
def reload_code def reload_code
if merge_request_diff && opened? if merge_request_diff && opened?
merge_request_diff.reload_content merge_request_diff.reload_content
...@@ -219,6 +228,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -219,6 +228,14 @@ class MergeRequest < ActiveRecord::Base
end end
end end
def source_project_namespace
if source_project && source_project.namespace
source_project.namespace.path
else
"(removed)"
end
end
def source_branch_exists? def source_branch_exists?
return false unless self.source_project return false unless self.source_project
......
...@@ -148,13 +148,11 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -148,13 +148,11 @@ class MergeRequestDiff < ActiveRecord::Base
Gitlab::Git::Diff.between(repository, source_branch, target_branch) Gitlab::Git::Diff.between(repository, source_branch, target_branch)
end end
if diffs == broken_diffs
self.state = :timeout
diffs = []
end
diffs ||= [] diffs ||= []
diffs diffs
rescue Gitlab::Git::Diff::TimeoutError => ex
self.state = :timeout
diffs = []
end end
def repository def repository
......
...@@ -9,13 +9,24 @@ class Notification ...@@ -9,13 +9,24 @@ class Notification
attr_accessor :target attr_accessor :target
def self.notification_levels class << self
def notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH] [N_DISABLED, N_PARTICIPATING, N_WATCH]
end end
def self.project_notification_levels def options_with_labels
{
disabled: N_DISABLED,
participating: N_PARTICIPATING,
watch: N_WATCH,
global: N_GLOBAL
}
end
def project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL] [N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL]
end end
end
def initialize(target) def initialize(target)
@target = target @target = target
...@@ -36,4 +47,8 @@ class Notification ...@@ -36,4 +47,8 @@ class Notification
def global? def global?
target.notification_level == N_GLOBAL target.notification_level == N_GLOBAL
end end
def level
target.notification_level
end
end end
...@@ -40,7 +40,7 @@ class HipchatService < Service ...@@ -40,7 +40,7 @@ class HipchatService < Service
end end
def execute(push_data) def execute(push_data)
gate[room].send('Gitlab', create_message(push_data)) gate[room].send('GitLab', create_message(push_data))
end end
private private
......
...@@ -78,6 +78,7 @@ class User < ActiveRecord::Base ...@@ -78,6 +78,7 @@ class User < ActiveRecord::Base
# Profile # Profile
has_many :keys, dependent: :destroy has_many :keys, dependent: :destroy
has_many :emails, dependent: :destroy
# Groups # Groups
has_many :users_groups, dependent: :destroy has_many :users_groups, dependent: :destroy
...@@ -116,6 +117,7 @@ class User < ActiveRecord::Base ...@@ -116,6 +117,7 @@ class User < ActiveRecord::Base
validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? } validate :namespace_uniq, if: ->(user) { user.username_changed? }
validate :avatar_type, if: ->(user) { user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar_changed? }
validate :unique_email, if: ->(user) { user.email_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i } validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
before_validation :generate_password, on: :create before_validation :generate_password, on: :create
...@@ -184,6 +186,13 @@ class User < ActiveRecord::Base ...@@ -184,6 +186,13 @@ class User < ActiveRecord::Base
end end
end end
def find_for_commit(email, name)
# Prefer email match over name match
User.where(email: email).first ||
User.joins(:emails).where(emails: { email: email }).first ||
User.where(name: name).first
end
def filter filter_name def filter filter_name
case filter_name case filter_name
when "admins"; self.admins when "admins"; self.admins
...@@ -250,6 +259,10 @@ class User < ActiveRecord::Base ...@@ -250,6 +259,10 @@ class User < ActiveRecord::Base
end end
end end
def unique_email
self.errors.add(:email, 'has already been taken') if Email.exists?(email: self.email)
end
# Groups user has access to # Groups user has access to
def authorized_groups def authorized_groups
@authorized_groups ||= begin @authorized_groups ||= begin
...@@ -443,4 +456,8 @@ class User < ActiveRecord::Base ...@@ -443,4 +456,8 @@ class User < ActiveRecord::Base
def short_website_url def short_website_url
website_url.gsub(/https?:\/\//, '') website_url.gsub(/https?:\/\//, '')
end end
def all_ssh_keys
keys.map(&:key)
end
end end
class EmailObserver < BaseObserver
def after_create(email)
notification.new_email(email)
end
end
...@@ -188,8 +188,6 @@ class GitPushService ...@@ -188,8 +188,6 @@ class GitPushService
end end
def commit_user commit def commit_user commit
User.where(email: commit.author_email).first || User.find_for_commit(commit.author_email, commit.author_name) || user
User.where(name: commit.author_name).first ||
user
end end
end end
...@@ -17,6 +17,13 @@ class NotificationService ...@@ -17,6 +17,13 @@ class NotificationService
end end
end end
# Always notify user about email added to profile
def new_email(email)
if email.user
mailer.new_email_email(email.id)
end
end
# When create an issue we should send next emails: # When create an issue we should send next emails:
# #
# * issue assignee if their notification level is not Disabled # * issue assignee if their notification level is not Disabled
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
%p.light %p.light
You can manage projects, users and other GitLab data from here. You can manage projects, users and other GitLab data from here.
%hr %hr
.admin_dash.row .admin-dashboard
.row
.col-sm-4 .col-sm-4
.light-well .light-well
%h4 Projects %h4 Projects
...@@ -29,13 +30,13 @@ ...@@ -29,13 +30,13 @@
%hr %hr
= link_to 'New Group', new_admin_group_path, class: "btn btn-new" = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
.row.prepend-top-10 .row.prepend-top-10
.col-md-4 .col-md-4
%h4 Latest projects %h4 Latest projects
%hr %hr
- @projects.each do |project| - @projects.each do |project|
%p %p
= link_to project.name_with_namespace, [:admin, project] = link_to project.name_with_namespace, [:admin, project], class: 'str-truncated'
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(project.created_at)} #{time_ago_with_tooltip(project.created_at)}
...@@ -44,7 +45,7 @@ ...@@ -44,7 +45,7 @@
%hr %hr
- @users.each do |user| - @users.each do |user|
%p %p
= link_to [:admin, user] do = link_to [:admin, user], class: 'str-truncated' do
= user.name = user.name
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(user.created_at)} #{time_ago_with_tooltip(user.created_at)}
...@@ -54,13 +55,13 @@ ...@@ -54,13 +55,13 @@
%hr %hr
- @groups.each do |group| - @groups.each do |group|
%p %p
= link_to [:admin, group] do = link_to [:admin, group], class: 'str-truncated' do
= group.name = group.name
%span.light.pull-right %span.light.pull-right
#{time_ago_with_tooltip(group.created_at)} #{time_ago_with_tooltip(group.created_at)}
%br %br
.row .row
.col-md-4 .col-md-4
%h4 Stats %h4 Stats
%hr %hr
......
%h3.page-title %h3.page-title
Groups (#{@groups.total_count}) Groups (#{@groups.total_count})
%small = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
allows you to keep projects organized.
%p.light
Group allows you to keep projects organized.
Use groups for uniting related projects. Use groups for uniting related projects.
= link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" %hr
%br
= form_tag admin_groups_path, method: :get, class: 'form-inline' do = form_tag admin_groups_path, method: :get, class: 'form-inline' do
.form-group .form-group
= text_field_tag :name, params[:name], class: "form-control input-mn-300" = text_field_tag :name, params[:name], class: "form-control input-mn-300"
...@@ -23,24 +24,18 @@ ...@@ -23,24 +24,18 @@
%h4 %h4
= link_to [:admin, group] do = link_to [:admin, group] do
%i.icon-folder-close
= group.name = group.name
&rarr; &rarr;
%span.monospace %span.monospace
%i.icon-folder-close
%strong #{group.path}/ %strong #{group.path}/
.clearfix.light.append-bottom-10
%span
%b Members:
%span.badge= group.members.size
\|
%span
%b Projects:
%span.badge= group.projects.count
.clearfix .clearfix
%p %p
= truncate group.description, length: 150 = truncate group.description, length: 150
.clearfix
%p.light
#{pluralize(group.members.size, 'member')}, #{pluralize(group.projects.count, 'project')}
= paginate @groups, theme: "gitlab" = paginate @groups, theme: "gitlab"
.row .row
.col-md-3 .col-md-3
.admin-filter .admin-filter
= form_tag admin_users_path, method: :get, class: 'form-inline' do
.append-bottom-10
.form-group
= search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control'
= button_tag type: 'submit', class: 'btn btn-primary' do
%i.icon-search
%ul.nav.nav-pills.nav-stacked %ul.nav.nav-pills.nav-stacked
%li{class: "#{'active' unless params[:filter]}"} %li{class: "#{'active' unless params[:filter]}"}
= link_to admin_users_path do = link_to admin_users_path do
...@@ -25,6 +19,12 @@ ...@@ -25,6 +19,12 @@
Without projects Without projects
%small.pull-right= User.without_projects.count %small.pull-right= User.without_projects.count
%hr %hr
= form_tag admin_users_path, method: :get, class: 'form-inline' do
.form-group
= search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control'
= button_tag type: 'submit', class: 'btn btn-primary' do
%i.icon-search
%hr
= link_to 'Reset', admin_users_path, class: "btn btn-cancel" = link_to 'Reset', admin_users_path, class: "btn btn-cancel"
.col-md-9 .col-md-9
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
%span.pull-right #{@issues.total_count} issues %span.pull-right #{@issues.total_count} issues
%p.light %p.light
List all issues from all project's you have access to. List all issues from all projects you have access to.
%hr %hr
.row .row
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%p.light %p.light
List all merge requests from all project's you have access to. List all merge requests from all projects you have access to.
%hr %hr
.row .row
.col-md-3 .col-md-3
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
.devise-errors .devise-errors
= devise_error_messages! = devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, placeholder: 'Email', class: "form-control", required: true = f.email_field :email, placeholder: 'Email', class: "form-control", required: true
.clearfix.append-bottom-10 .clearfix.append-bottom-10
= f.submit "Resend confirmation instructions", class: 'btn btn-success' = f.submit "Resend confirmation instructions", class: 'btn btn-success'
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%h3.page-title Reset password %h3.page-title Reset password
.devise-errors .devise-errors
= devise_error_messages! = devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, placeholder: "Email", class: "form-control", required: true = f.email_field :email, placeholder: "Email", class: "form-control", required: true
.clearfix.append-bottom-10 .clearfix.append-bottom-10
= f.submit "Reset password", class: "btn-primary btn" = f.submit "Reset password", class: "btn-primary btn"
......
.login-box .login-box
%h3.page-title Sign in %h3.page-title Sign in
- if ldap_enabled? - if ldap_enabled?
%ul.nav.nav-tabs.append-bottom-20 %ul.nav.nav-tabs
%li.active %li.active
= link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab' = link_to 'LDAP', '#tab-ldap', 'data-toggle' => 'tab'
%li %li
......
= form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal users-group-form' } do |f| = form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
%h4.append-bottom-20
New member(s) for
%strong #{@group.name}
group
%p 1. Choose users you want in the group
.form-group .form-group
= f.label :user_ids, "People", class: 'control-label' = f.label :user_ids, "People", class: 'control-label'
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
%p 2. Set access level for them
.form-group .form-group
= f.label :group_access, "Group Access", class: 'control-label' = f.label :group_access, "Group Access", class: 'control-label'
.col-sm-10= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select select2" .col-sm-10= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select select2"
......
...@@ -6,15 +6,34 @@ ...@@ -6,15 +6,34 @@
%strong= link_to "here", help_permissions_path, class: "vlink" %strong= link_to "here", help_permissions_path, class: "vlink"
%hr %hr
- can_manage_group = current_user.can? :manage_group, @group
.ui-box .clearfix
= form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find member by name', class: 'form-control search-text-input input-mn-300' }
= submit_tag 'Search', class: 'btn'
- if current_user.can? :manage_group, @group
.pull-right
= link_to '#', class: 'btn btn-new js-toggle-visibility-link' do
Add members
%i.icon-chevron-down
.js-toggle-visibility-container.hide.new-group-member-holder
= render "new_group_member"
.ui-box.prepend-top-20
.title .title
%strong #{@group.name} %strong #{@group.name}
group members group members
%small %small
(#{@members.count}) (#{@members.total_count})
%ul.well-list %ul.well-list
- @members.each do |member| - @members.each do |member|
= render 'users_groups/users_group', member: member, show_controls: can_manage_group = render 'users_groups/users_group', member: member, show_controls: true
- if can_manage_group = paginate @members, theme: 'gitlab'
= render "new_group_member"
:coffeescript
$('form.member-search-form').on 'submit', (event) ->
event.preventDefault()
Turbolinks.visit @.action + '?' + $(@).serialize()
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%i.icon-angle-left %i.icon-angle-left
Back to help Back to help
%ul.nav.nav-pills.nav-stacked %ul.nav.nav-pills.nav-stacked
- %w(README projects project_snippets repositories deploy_keys users groups session issues milestones merge_requests notes system_hooks).each do |file| - %w(README projects project_snippets repositories repository_files commits deploy_keys users groups session issues milestones merge_requests notes system_hooks).each do |file|
%li{class: file == @category ? 'active' : nil} %li{class: file == @category ? 'active' : nil}
= link_to file.titleize, help_api_file_path(file) = link_to file.titleize, help_api_file_path(file)
......
...@@ -8,4 +8,5 @@ ...@@ -8,4 +8,5 @@
= link_to title, path = link_to title, path
.col-md-9 .col-md-9
.wiki
= yield = yield
...@@ -217,3 +217,4 @@ ...@@ -217,3 +217,4 @@
%td %td
%td %td
%td.permission-x &#10003; %td.permission-x &#10003;
%p.light Any user can remove himself from a group, unless he is the last Owner of the group.
...@@ -44,3 +44,18 @@ ...@@ -44,3 +44,18 @@
%li Go to your project dashboard %li Go to your project dashboard
%li Click on the "Edit" tab %li Click on the "Edit" tab
%li Change "Visibility Level" %li Change "Visibility Level"
%h4 Visibility of users
The public page of users, located at
= succeed "," do
%code u/username
is visible if either:
%ul
%li
You are logged in.
%li
%p
You are logged out, and the target user is authorized to (is Guest, Reporter, etc.)
at least one public project.
%p Otherwise, you will be redirected to the sign in page.
When visiting the public page of an user, you will only see listed projects which you can view yourself.
= render layout: 'help/layout' do = render layout: 'help/layout' do
%h3.page-title Workflow %h3.page-title Workflow
%h4 Summary
%ol.help %ol.help
%li %li
%p Clone project %p Clone project
...@@ -35,3 +37,37 @@ ...@@ -35,3 +37,37 @@
%li %li
%p Your team lead will review code &amp; merge it to main branch %p Your team lead will review code &amp; merge it to main branch
%h3 Authorization for Merge Requests
%p
There are two main ways to have a merge request flow with GitLab: working with protected branches in a single repository, or working with forks of an authoritative project.
%h4 Protected branch flow
%p
With the protected branch flow everybody works within the same GitLab project.
The project maintainers get Master access and the regular developers get Developer access.
The maintainers mark the authoritative branches as 'Protected'.
The developers push feature branches to the project and create merge requests to have their feature branches reviewed and merged into one of the protected branches.
Only users with Master access can merge changes into a protected branch.
%h5 Advantages
%ul
%li fewer projects means less clutter
%li developers need to consider only one remote repository
%h5 Disadvantages
%ul
%li manual setup of protected branch required for each new project
%h4 Forking workflow
%p
With the forking workflow the maintainers get Master access and the regular developers get Reporter access to the authoritative repository, which prohibits them from pushing any changes to it.
Developers create forks of the authoritative project and push their feature branches to their own forks.
To get their changes into master they need to create a merge request across forks.
%h5 Advantages
%ul
%li in an appropriately configured GitLab group, new projects automatically get the required access restrictions for regular developers: fewer manual steps to configure authorization for new projects
%h5 Disadvantages
%ul
%li all developers on the project need to keep their forks up to date, which requires more advanced Git skills (managing multiple remotes)
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
= 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 %span.separator
%h1.project_name= 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"}
%span.sr-only Toggle navigation %span.sr-only Toggle navigation
......
...@@ -6,11 +6,7 @@ ...@@ -6,11 +6,7 @@
= link_to public_root_path, class: "home" do = link_to public_root_path, class: "home" do
%h1 GITLAB %h1 GITLAB
%span.separator %span.separator
%h1.project_name %h1.title= title
- if @project
= project_title(@project)
- else
Public Projects
.pull-right .pull-right
= link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new' = link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new'
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%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} admin", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Admin area" = render "layouts/head_panel", title: "Admin area"
= render "layouts/flash" = render "layouts/flash"
%nav.main-nav.navbar-collapse.collapse %nav.main-nav.navbar-collapse.collapse
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
%i.icon-home %i.icon-home
= nav_link(controller: :accounts) do = nav_link(controller: :accounts) do
= link_to "Account", profile_account_path = link_to "Account", profile_account_path
= nav_link(controller: :emails) do
= link_to profile_emails_path do
Emails
%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 "Password", edit_profile_password_path
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%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} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: @title = render "layouts/head_panel", title: @title
= render "layouts/flash" = render "layouts/flash"
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "Public Projects" = render "layouts/head", title: "Public Projects"
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- if current_user - if current_user
= render "layouts/head_panel", title: "Public Projects" = render "layouts/head_panel", title: "Public Projects"
- else - else
= render "layouts/public_head_panel" = render "layouts/public_head_panel", title: "Public Projects"
.container.navless-container .container.navless-container
.content= yield .content= yield
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
%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} application", :'data-page' => body_data_page}
= render "layouts/public_head_panel" = render "layouts/broadcast"
%nav.main-nav.navbar-collapse.collapse = render "layouts/public_head_panel", title: @project.name_with_namespace
%nav.main-nav
.container= render 'layouts/nav/project' .container= render 'layouts/nav/project'
.container .container
.content= yield .content= yield
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: @title
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/public_head_panel", title: @title
.container.navless-container
.content= yield
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%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} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Search" = render "layouts/head_panel", title: "Search"
= render "layouts/flash" = render "layouts/flash"
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head", title: "#{@team.name}" = render "layouts/head", title: "#{@team.name}"
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: "team: #{@team.name}" = render "layouts/head_panel", title: "team: #{@team.name}"
= render "layouts/flash" = render "layouts/flash"
%nav.main-nav.navbar-collapse.collapse %nav.main-nav.navbar-collapse.collapse
......
%p
Hi #{@user.name}!
%p
A new email was added to your account:
%p
email:
%code= @email.email
%p
If this email was added in error, you can remove it here:
= link_to "Emails", profile_emails_url
Hi <%= @user.name %>!
A new email was added to your account:
email.................. <%= @email.email %>
If this email was added in error, you can remove it here: <%= profile_emails_url %>
...@@ -21,3 +21,8 @@ ...@@ -21,3 +21,8 @@
%pre %pre
= diff.diff = diff.diff
%br %br
- if @compare.timeout
%h5 Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit?
%h5 Diff for big amount of commits is disabled
...@@ -18,3 +18,8 @@ Diff: ...@@ -18,3 +18,8 @@ Diff:
= diff.new_path || diff.old_path = diff.new_path || diff.old_path
\===================================== \=====================================
= diff.diff = diff.diff
\
- if @compare.timeout
Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit?
Diff for big amount of commits is disabled
%h3.page-title
My email addresses
%p.light
Your
%b Primary Email
will be used for account notifications, avatar detection and web based operations, such as edits and merges.
%br
All email addresses will be used to identify your commits.
%hr
.ui-box
.title
Emails (#{@emails.count + 1})
%ul.well-list#emails-table
%li
%strong= @primary
%span.label.label-success Primary Email
- @emails.each do |email|
%li
%strong= email.email
%span.cgray
added #{time_ago_with_tooltip(email.created_at)}
= link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-small btn-remove pull-right'
%h4 Add email address
= form_for 'email', url: profile_emails_path, html: { class: 'form-horizontal' } do |f|
.form-group
= f.label :email, class: 'control-label'
.col-sm-10
= f.text_field :email, class: 'form-control'
.form-actions
= f.submit 'Add', class: 'btn btn-create'
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
%i.icon-cogs %i.icon-cogs
Settings Settings
= link_to leave_profile_group_path(group), data: { confirm: "Are you sure you want to leave #{group.name} group?"}, method: :delete, class: "btn-small btn grouped", title: 'Remove user from group' do - if can?(current_user, :destroy, user_group)
= link_to leave_profile_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-small btn grouped", title: 'Remove user from group' do
%i.icon-signout %i.icon-signout
Leave Leave
......
%li %li
.row %span.notification-icon-holder
.col-sm-4 - if notification.global?
%span = notification_icon(@notification)
- else
= notification_icon(notification) = notification_icon(notification)
%span.str-truncated
- if membership.kind_of? UsersGroup - if membership.kind_of? UsersGroup
= link_to membership.group.name, membership.group = link_to membership.group.name, membership.group
- else - else
= link_to_project(membership.project) = link_to_project(membership.project)
.col-sm-8 .pull-right
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do = form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
= hidden_field_tag :notification_type, type, id: dom_id(membership, 'notification_type') = hidden_field_tag :notification_type, type, id: dom_id(membership, 'notification_type')
= hidden_field_tag :notification_id, membership.id, id: dom_id(membership, 'notification_id') = hidden_field_tag :notification_id, membership.id, id: dom_id(membership, 'notification_id')
= select_tag :notification_level, options_for_select(Notification.options_with_labels, notification.level), class: 'trigger-submit'
= label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_GLOBAL, notification.global?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Use global setting
= label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_DISABLED, notification.disabled?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Disabled
= label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, notification.participating?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Participating
= label_tag nil, class: 'radio-inline' do
= radio_button_tag :notification_level, Notification::N_WATCH, notification.watch?, id: dom_id(membership, 'notification_level'), class: 'trigger-submit'
%span Watch
...@@ -3,54 +3,47 @@ ...@@ -3,54 +3,47 @@
%p.light %p.light
GitLab uses the email specified in your profile for notifications GitLab uses the email specified in your profile for notifications
%hr %hr
.alert.alert-info = form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications form-horizontal global-notifications-form' do
%p
%i.icon-circle.cred
%strong Disabled
&ndash; You will not get any notifications via email
%p
%i.icon-circle.cblue
%strong Participating
&ndash; You will only receive notifications from related resources (e.g. from your commits or assigned issues)
%p
%i.icon-circle.cgreen
%strong Watch
&ndash; You will receive all notifications from projects in which you participate
.row
.col-sm-4
%h4
= notification_icon(@notification)
Global setting
.col-sm-8
= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
= hidden_field_tag :notification_type, 'global' = hidden_field_tag :notification_type, 'global'
= label_tag nil, class: 'radio-inline' do = label_tag :notification_level, 'Notification level', class: 'control-label'
.col-sm-10
.radio
= label_tag nil, class: '' do
= radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?, class: 'trigger-submit' = radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?, class: 'trigger-submit'
%span Disabled .level-title
Disabled
%p You will not get any notifications via email
= label_tag nil, class: 'radio-inline' do .radio
= label_tag nil, class: '' do
= radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?, class: 'trigger-submit' = radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?, class: 'trigger-submit'
%span Participating .level-title
Participating
%p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
= label_tag nil, class: 'radio-inline' do .radio
= label_tag nil, class: '' do
= radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?, class: 'trigger-submit' = radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?, class: 'trigger-submit'
%span Watch .level-title
Watch
%p You will receive all notifications from projects in which you participate
%br .clearfix
= link_to '#', class: 'js-toggle-visibility-link' do
%span.btn.btn-tiny
%i.icon-chevron-down
%span Advanced notifications settings
.js-toggle-visibility-container.hide
%hr %hr
%p
You can also specify notification level per group or per project
%br
By default all projects and groups uses notification level set above
.row.all-notifications
.col-md-6
%h4 Groups: %h4 Groups:
%ul.bordered-list %ul.bordered-list
- @users_groups.each do |users_group| - @users_groups.each do |users_group|
- notification = Notification.new(users_group) - notification = Notification.new(users_group)
= render 'settings', type: 'group', membership: users_group, notification: notification = render 'settings', type: 'group', membership: users_group, notification: notification
.col-md-6
%h4 Projects: %h4 Projects:
%ul.bordered-list %ul.bordered-list
- @users_projects.each do |users_project| - @users_projects.each do |users_project|
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
%hr %hr
= form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f| = form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f|
%p.slead %p.slead
Please set new password before proceed. Please set a new password before proceeding.
%br %br
After successful password update you will be redirected to login screen After a successful password update you will be redirected to login screen.
-if @user.errors.any? -if @user.errors.any?
.alert.alert-danger .alert.alert-danger
%ul %ul
......
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
.pull-right .pull-right
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
= render 'projects/repositories/download_archive', ref: branch.name, btn_class: 'grouped btn-group-small' = render 'projects/repositories/download_archive', ref: branch.name, btn_class: 'grouped btn-group-small'
= link_to project_compare_index_path(@project, from: branch.name, to: branch.name), class: 'btn grouped btn-small', title: "Compare" do - if branch.name != @repository.root_ref
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name), class: 'btn grouped btn-small', method: :post, title: "Compare" do
%i.icon-copy %i.icon-copy
Compare Compare
......
%ul.nav.nav-tabs.append-bottom-15 %ul.nav.nav-tabs
%li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'} %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
= nav_link(controller: [:commit, :commits]) do = nav_link(controller: [:commit, :commits]) do
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
%i.icon-user %i.icon-user
Assign to Assign to
.col-sm-10 .col-sm-10
= f.select(:assignee_id, assignee_options(@issue), { include_blank: "Select a user" }, {class: 'select2'}) = project_users_select_tag('issue[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @issue.assignee_id)
&nbsp; &nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group .form-group
......
%ul.nav.nav-tabs.append-bottom-15 %ul.nav.nav-tabs
= 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
Browse Issues Browse Issues
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
%li.pull-right %li.pull-right
.pull-right .pull-right
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'inline issue-search-form' do = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
.append-right-10.hidden-xs.hidden-sm .append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
- if can? current_user, :write_issue, @project - if can? current_user, :write_issue, @project
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.icon-plus %i.icon-plus
New Issue New Issue
= 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|
Created by #{link_to_member(@project, issue.author)}&nbsp; %strong.append-right-10
- if issue.assignee Assignee:
\ and currently assigned to
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= link_to profile_path(issue.assignee) do = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @issue.assignee_id)
= image_tag(avatar_icon(issue.assignee.email), class: 'avatar avatar-inline s16 assignee') if issue.assignee
= f.select(:assignee_id, assignee_options(@issue), { include_blank: "Assign to user (none):" }, {class: 'select2'})
- elsif issue.assignee - elsif issue.assignee
= link_to_member(@project, @issue.assignee) = link_to_member(@project, @issue.assignee)
- else
None
.pull-right
.pull-right.hidden-sm %strong.append-right-10
- if issue.milestone Milestone:
- milestone = issue.milestone
%cite.cgray Attached to 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 (none):" }, {class: 'select2 select2-compact'}) = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'})
= hidden_field_tag :issue_context = hidden_field_tag :issue_context
= f.submit class: 'btn' = f.submit class: 'btn'
- elsif issue.milestone - elsif issue.milestone
= link_to issue.milestone.title, project_milestone_path = link_to issue.milestone.title, project_milestone_path
- else
None
.ui-box .append-bottom-10
.title .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"
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_project_issues_path(@project), method: :post do
%span Update selected issues with &nbsp;
= select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
= select_tag('update[assignee_id]', bulk_update_assignee_options, prompt: "Assignee")
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :status, params[:status]
= button_tag "Save", class: "btn update_selected_issues btn-small btn-save"
.issues-filters .issues-filters
%span Filter by .dropdown.inline
.dropdown.inline.prepend-left-10 %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%i.icon-tags
%span.light labels:
- if params[:label_name].present?
%strong= params[:label_name]
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to project_filter_path(label_name: nil) do
Any
- issue_label_names.each do |label_name|
%li
= link_to project_filter_path(label_name: label_name) do
%span{class: "label #{label_css_class(label_name)}"}
%i.icon-tag
= label_name
.dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%i.icon-user %i.icon-user
%span.light assignee: %span.light assignee:
- if @assignee.present? - if @assignee.present?
...@@ -49,14 +19,14 @@ ...@@ -49,14 +19,14 @@
Any Any
= link_to project_filter_path(assignee_id: 0) do = link_to project_filter_path(assignee_id: 0) do
Unassigned Unassigned
- @project.team.members.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 project_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
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.icon-time %i.icon-time
%span.light milestone: %span.light milestone:
- if @milestone.present? - if @milestone.present?
...@@ -81,7 +51,17 @@ ...@@ -81,7 +51,17 @@
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_project_issues_path(@project), method: :post do
= select_tag('update[status]', options_for_select(['Open', 'Closed']), prompt: "Status")
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :status, params[:status]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.ui-box
%ul.well-list.issues-list %ul.well-list.issues-list
= render @issues = render @issues
- if @issues.blank? - if @issues.blank?
......
= render "head" = render "head"
.row .row
.col-md-3 .col-md-3
= render 'shared/project_filter', project_entities_path: project_issues_path(@project) = render 'shared/project_filter', project_entities_path: project_issues_path(@project), labels: true
.col-md-9.issues-holder .col-md-9.issues-holder
= render "issues" = render "issues"
%h3.page-title %h3.page-title
Issue ##{@issue.iid} Issue ##{@issue.iid}
%small
created #{time_ago_with_tooltip(@issue.created_at)}
- if @issue.closed?
%span.state-label.state-label-red Closed
- else
%span.state-label.state-label-green Open
%span.pull-right %span.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 grouped", title: "New Issue", id: "new_issue_link" do = link_to new_project_issue_path(@project), class: "btn grouped", title: "New Issue", id: "new_issue_link" do
...@@ -16,9 +8,9 @@ ...@@ -16,9 +8,9 @@
New Issue New Issue
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
- if @issue.closed? - if @issue.closed?
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue" = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped btn-reopen"
- else - else
= link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped btn-close", title: "Close Issue"
= link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
%i.icon-edit %i.icon-edit
...@@ -38,25 +30,34 @@ ...@@ -38,25 +30,34 @@
= @issue.milestone.title = @issue.milestone.title
.issue-box .issue-box
.state{ class: issue_alert_class(@issue) }
- if @issue.closed?
%span.state-label.state-label-red Closed
- else
%span.state-label.state-label-green Open
%span.creator
Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)}
%h4.title %h4.title
= gfm escape_once(@issue.title) = gfm escape_once(@issue.title)
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
- if @issue.description.present? - if @issue.description.present?
.description .description
.wiki .wiki
= preserve do = preserve do
= markdown @issue.description = markdown @issue.description
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
- content_for :note_actions do - content_for :note_actions do
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
- if @issue.closed? - if @issue.closed?
= link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue" = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped btn-reopen"
- else - else
= link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped btn-close", title: "Close Issue"
.participants .participants
%cite.cgray #{@issue.participants.count} participants %cite.cgray #{@issue.participants.count} participants
......
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
:plain :plain
$("##{dom_id(@issue)}").fadeOut(); $("##{dom_id(@issue)}").fadeOut();
- elsif params[:issue_context] - elsif params[:issue_context]
$('.issue-box .context').html("#{escape_javascript(render partial: 'issue_context', locals: { issue: @issue })}");
$('.issue-box .context').effect('highlight'); $('.issue-box .context').effect('highlight');
$('.select2').select2();
$('.edit-issue.inline-update input[type="submit"]').hide();
- 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
......
...@@ -6,19 +6,22 @@ ...@@ -6,19 +6,22 @@
%li= msg %li= msg
.merge-request-branches .merge-request-branches
.row .form-group
.col-md-5 = label_tag nil, class: 'control-label' do
From
.col-sm-10
.clearfix .clearfix
.pull-left .pull-left
= f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? }) = f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted? })
.pull-left .pull-left
&nbsp; &nbsp;
= f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'}) = f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2'})
.mr_source_commit.prepend-top-10 .mr_source_commit
.col-md-2 %br
.merge-request-angle .form-group
%i.icon-long-arrow-right = label_tag nil, class: 'control-label' do
.col-md-5 To
.col-sm-10
.clearfix .clearfix
.pull-left .pull-left
- projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project] - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project]
...@@ -26,7 +29,7 @@ ...@@ -26,7 +29,7 @@
.pull-left .pull-left
&nbsp; &nbsp;
= f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'}) = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2'})
.mr_target_commit.prepend-top-10 .mr_target_commit
%hr %hr
.merge-request-form-info .merge-request-form-info
...@@ -47,7 +50,7 @@ ...@@ -47,7 +50,7 @@
%i.icon-user %i.icon-user
Assign to Assign to
.col-sm-10 .col-sm-10
= f.select(:assignee_id, assignee_options(@merge_request), { include_blank: "Select a user" }, {class: 'select2'}) = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id)
&nbsp; &nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.form-group .form-group
......
...@@ -10,14 +10,14 @@ ...@@ -10,14 +10,14 @@
%span.pull-right %span.pull-right
- if merge_request.for_fork? - if merge_request.for_fork?
%span.light %span.light
= "#{merge_request.source_project_path}" #{merge_request.source_project_namespace}:
= "#{merge_request.source_branch}" = merge_request.source_branch
%i.icon-angle-right.light %i.icon-angle-right.light
= "#{merge_request.target_branch}" = merge_request.target_branch
- else - else
= "#{merge_request.source_branch}" = merge_request.source_branch
%i.icon-angle-right.light %i.icon-angle-right.light
= "#{merge_request.target_branch}" = merge_request.target_branch
.merge-request-info .merge-request-info
- if merge_request.author - if merge_request.author
authored by #{link_to_member(merge_request.source_project, merge_request.author)} authored by #{link_to_member(merge_request.source_project, merge_request.author)}
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= render "projects/merge_requests/show/commits" = render "projects/merge_requests/show/commits"
- if @commits.present? - if @commits.present?
%ul.nav.nav-tabs.append-bottom-10 %ul.nav.nav-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.icon-comment %i.icon-comment
...@@ -22,6 +22,12 @@ ...@@ -22,6 +22,12 @@
%i.icon-list-alt %i.icon-list-alt
Diff Diff
- content_for :note_actions do
- if can?(current_user, :modify_merge_request, @merge_request)
- unless @merge_request.closed?
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request"
.notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form"
.diffs.tab-content .diffs.tab-content
......
:plain :plain
$(".mr_source_commit").html("#{commit_to_html(@commit, @source_project)}"); $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
var mrTitle = $('#merge_request_title'); var mrTitle = $('#merge_request_title');
if(mrTitle.val().length == 0) { if(mrTitle.val().length == 0) {
......
:plain :plain
$(".mr_target_commit").html("#{commit_to_html(@commit, @target_project)}"); $(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}");
...@@ -10,12 +10,9 @@ ...@@ -10,12 +10,9 @@
.col-md-3 .col-md-3
= render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project) = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project)
.col-md-9 .col-md-9
.ui-box .mr-filters.append-bottom-10
.title .dropdown.inline
.mr-filters %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span Filter by
.dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
%i.icon-user %i.icon-user
%span.light assignee: %span.light assignee:
- if @assignee.present? - if @assignee.present?
...@@ -31,14 +28,14 @@ ...@@ -31,14 +28,14 @@
Any Any
= link_to project_filter_path(assignee_id: 0) do = link_to project_filter_path(assignee_id: 0) do
Unassigned Unassigned
- @project.team.members.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 project_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
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.icon-time %i.icon-time
%span.light milestone: %span.light milestone:
- if @milestone.present? - if @milestone.present?
...@@ -63,6 +60,7 @@ ...@@ -63,6 +60,7 @@
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
.ui-box
%ul.well-list.mr-list %ul.well-list.mr-list
= render @merge_requests = render @merge_requests
- if @merge_requests.blank? - if @merge_requests.blank?
......
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
%strong.append-right-10
Assignee:
- if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control', selected: @merge_request.assignee_id)
- elsif merge_request.assignee
= link_to_member(@project, @merge_request.assignee)
- else
None
.pull-right
%strong.append-right-10
Milestone:
- if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone (none):" }, {class: 'select2 select2-compact'})
= hidden_field_tag :merge_request_context
= f.submit class: 'btn'
- elsif merge_request.milestone
= link_to merge_request.milestone.title, project_milestone_path
- else
None
...@@ -3,8 +3,10 @@ ...@@ -3,8 +3,10 @@
- elsif @merge_request_diff.empty? - elsif @merge_request_diff.empty?
%h4.nothing_here_message Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} %h4.nothing_here_message Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
- else - else
%h4.nothing_here_message .bs-callout.bs-callout-warning
Can't load diff. %h4
Diff for this comparison is extremely large.
%p
You can You can
= link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request), format: :diff, class: "vlink" = link_to "download it", project_merge_request_path(@merge_request.source_project, @merge_request, format: :diff), class: "vlink"
instead. instead.
.issue-box .issue-box
.state{ class: merge_request_alert_class(@merge_request) }
- if @merge_request.merged?
%span.state-label.state-label-blue
Merged
- elsif @merge_request.closed?
%span.state-label.state-label-red
Closed
- else
%span.state-label.state-label-green
Open
%span.creator
Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
%h4.title %h4.title
= gfm escape_once(@merge_request.title) = gfm escape_once(@merge_request.title)
.context
%cite.cgray
Created by #{link_to_member(@project, @merge_request.author)}.
- if @merge_request.assignee
Currently assigned to #{link_to_member(@project, @merge_request.assignee)}.
- if @merge_request.milestone
.pull-right
- milestone = @merge_request.milestone
%cite.cgray Attached to milestone
%strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
- if @merge_request.description.present? - if @merge_request.description.present?
.description .description
.wiki .wiki
= preserve do = preserve do
= markdown @merge_request.description = markdown @merge_request.description
- if @merge_request.closed? .context
.description.alert-danger %cite.cgray
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
- if @merge_request.closed?
.alert.alert-info
%span %span
%i.icon-remove %i.icon-remove
Closed by #{link_to_member(@project, @merge_request.closed_event.author)} Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
#{time_ago_with_tooltip(@merge_request.closed_event.created_at)}. #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}.
- if @merge_request.merged? - if @merge_request.merged?
.description.alert-success .alert.alert-info
%span %span
%i.icon-ok %i.icon-ok
Merged by #{link_to_member(@project, @merge_request.merge_event.author)} Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
#{time_ago_with_tooltip(@merge_request.merge_event.created_at)}. #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}.
- if !@closes_issues.empty? && @merge_request.opened? - if !@closes_issues.empty? && @merge_request.opened?
.description.alert-info .alert.alert-info.alert-info
%span %span
%i.icon-ok %i.icon-ok
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'} Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
......
%h3.page-title %h3.page-title
= "Merge Request ##{@merge_request.iid}" = "Merge Request ##{@merge_request.iid}"
%small
created #{time_ago_with_tooltip(@merge_request.created_at)}
- if @merge_request.merged?
%span.state-label.state-label-green
%i.icon-ok
Merged
- elsif @merge_request.closed?
%span.state-label.state-label-red
Closed
- else
%span.state-label.state-label-green
Open
%span.pull-right %span.pull-right
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
...@@ -34,7 +19,7 @@ ...@@ -34,7 +19,7 @@
%i.icon-edit %i.icon-edit
Edit Edit
.votes-holder .votes-holder.hidden-sm.hidden-xs
#votes= render 'votes/votes_block', votable: @merge_request #votes= render 'votes/votes_block', votable: @merge_request
.back-link .back-link
......
- if params[:merge_request_context]
$('.issue-box .context').effect('highlight');
...@@ -42,14 +42,13 @@ ...@@ -42,14 +42,13 @@
.progress.progress-info .progress.progress-info
.progress-bar{style: "width: #{@milestone.percent_complete}%;"} .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
- if @milestone.description.present? - if @milestone.description.present?
.description .description
.wiki
= preserve do = preserve do
= markdown @milestone.description = markdown @milestone.description
%ul.nav.nav-tabs
%ul.nav.nav-tabs.append-bottom-10
%li.active %li.active
= link_to '#tab-issues', 'data-toggle' => 'tab' do = link_to '#tab-issues', 'data-toggle' => 'tab' do
Issues Issues
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
.note-body .note-body
.note-text .note-text
= preserve do = preserve do
= markdown(note.note) = markdown(note.note, {no_header_anchors: true})
.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|
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- split_button = split_button || false - split_button = split_button || false
- if split_button == true - if split_button == true
%span.btn-group{class: btn_class} %span.btn-group{class: btn_class}
= link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn' do = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span Download zip %span Download zip
%a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
...@@ -12,26 +12,26 @@ ...@@ -12,26 +12,26 @@
Select Archive Format Select Archive Format
%ul.dropdown-menu{ role: 'menu' } %ul.dropdown-menu{ role: 'menu' }
%li %li
= link_to archive_project_repository_path(@project, ref: ref, format: 'zip') do = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span Download zip %span Download zip
%li %li
= link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz') do = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span Download tar.gz %span Download tar.gz
%li %li
= link_to archive_project_repository_path(@project, ref: ref, format: 'tar.bz2') do = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span Download tar.bz2 %span Download tar.bz2
%li %li
= link_to archive_project_repository_path(@project, ref: ref, format: 'tar') do = link_to archive_project_repository_path(@project, ref: ref, format: 'tar'), rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span Download tar %span Download tar
- else - else
%span.btn-group{class: btn_class} %span.btn-group{class: btn_class}
= link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn' do = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span zip %span zip
= link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), class: 'btn' do = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do
%i.icon-download-alt %i.icon-download-alt
%span tar.gz %span tar.gz
\ No newline at end of file
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
%p %p
%span.light Owned by %span.light Owned by
- if @project.group - if @project.group
#{link_to @project.group.name, @project.group} Group #{link_to @project.group.name, @project.group} group
- else - else
#{link_to @project.owner_name, @project.owner} #{link_to @project.owner_name, @project.owner}
......
- group_users_count = @group.users_groups.count
.ui-box .ui-box
.title .title
%strong #{@group.name} %strong #{@group.name}
group members (#{@group.users_groups.count}) group members (#{group_users_count})
.pull-right .pull-right
= link_to members_group_path(@group), class: 'btn btn-small' do = link_to members_group_path(@group), class: 'btn btn-small' do
%i.icon-edit %i.icon-edit
%ul.well-list %ul.well-list
- @group.users_groups.order('group_access DESC').each do |member| - @group.users_groups.order('group_access DESC').limit(20).each do |member|
= render 'users_groups/users_group', member: member, show_controls: false = render 'users_groups/users_group', member: member, show_controls: false
- if group_users_count > 20
%li
and #{group_users_count - 20} more. For full list visit #{link_to 'group members page', members_group_path(@group)}
.readme-holder#README .readme-holder#README
%h4 %h4.readme-file-title
%i.icon-file %i.icon-file
= readme.name = readme.name
.wiki .wiki
......
%ul.nav.nav-tabs.append-bottom-20 %ul.nav.nav-tabs
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', project_wiki_path(@project, :home) = link_to 'Home', project_wiki_path(@project, :home)
......
%ul.nav.nav-tabs.append-bottom-10 %ul.nav.nav-tabs
%li{class: ("active" if params[:search_code].present?)} %li{class: ("active" if params[:search_code].present?)}
= link_to search_path(params.merge(search_code: true)) do = link_to search_path(params.merge(search_code: true)) do
Repository Code Repository Code
......
%div.highlighted-data{class: user_color_scheme_class} %div.highlighted-data{class: user_color_scheme_class}
.line-numbers .line-numbers
- blob.data.lines.size.times do |index| - blob.data.lines.to_a.size.times do |index|
- offset = defined?(first_line_number) ? first_line_number : 1 - offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset - i = index + offset
= link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do = link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do
......
...@@ -26,6 +26,20 @@ ...@@ -26,6 +26,20 @@
= link_to project_filter_path(state: 'all') do = link_to project_filter_path(state: 'all') do
All All
- if defined?(labels)
%fieldset
%legend Labels
%ul.nav.nav-pills.nav-stacked.nav-small.labels-filter
- issue_label_names.each do |label_name|
%li{class: label_filter_class(label_name)}
= link_to labels_filter_path(label_name) do
%span{class: "label #{label_css_class(label_name)}"}
%i.icon-tag
= label_name
- if selected_label?(label_name)
.pull-right
%i.icon-remove
%fieldset %fieldset
- if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any? - 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 = link_to project_entities_path, class: 'cgray pull-right' do
......
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= @sort = @sort
......
...@@ -9,10 +9,15 @@ ...@@ -9,10 +9,15 @@
%span.pull-right %span.pull-right
%strong= member.human_access %strong= member.human_access
- if show_controls
- if show_controls && can?(current_user, :manage_group, @group) && current_user != user - if can?(current_user, :modify, member)
= link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do = link_to '#', class: "btn-tiny btn js-toggle-button", title: 'Edit access level' do
%i.icon-edit %i.icon-edit
- if can?(current_user, :destroy, member)
- if current_user == member.user
= link_to leave_profile_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white
- else
= link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do = link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white %i.icon-minus.icon-white
......
...@@ -13,13 +13,13 @@ class EmailsOnPushWorker ...@@ -13,13 +13,13 @@ class EmailsOnPushWorker
return true return true
end end
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha) compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha, MergeRequestDiff::COMMITS_SAFE_SIZE)
# Do not send emails if git compare failed # Do not send emails if git compare failed
return false unless compare && compare.commits.present? return false unless compare && compare.commits.present?
recipients.split(" ").each do |recipient| recipients.split(" ").each do |recipient|
Notify.delay.repository_push_email(project_id, recipient, author_id, branch, compare) Notify.repository_push_email(project_id, recipient, author_id, branch, compare).deliver
end end
end end
end end
#!/usr/bin/env ruby
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
load Gem.bin_path('bundler', 'bundle')
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
require_relative '../config/boot'
require 'rake'
Rake.application.run
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
require 'bundler/setup'
load Gem.bin_path('rspec', 'rspec')
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
require 'bundler/setup'
load Gem.bin_path('spinach', 'spinach')
#!/usr/bin/env ruby
# This file loads spring without using Bundler, in order to be fast
# It gets overwritten when you run the `spring binstub` command
unless defined?(Spring)
require "rubygems"
require "bundler"
if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m)
ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
ENV["GEM_HOME"] = ""
Gem.paths = ENV
gem "spring", match[1]
require "spring/binstub"
end
end
...@@ -62,6 +62,8 @@ module Gitlab ...@@ -62,6 +62,8 @@ module Gitlab
# Enable the asset pipeline # Enable the asset pipeline
config.assets.enabled = true config.assets.enabled = true
config.assets.paths << Emoji.images_path
config.assets.precompile << "emoji/*.png"
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.0'
......
...@@ -12,6 +12,9 @@ Gitlab::Application.routes.draw do ...@@ -12,6 +12,9 @@ Gitlab::Application.routes.draw do
API::API.logger Rails.logger API::API.logger Rails.logger
mount API::API => '/api' mount API::API => '/api'
# Get all keys of user
get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ }
constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? } constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? }
constraints constraint do constraints constraint do
mount Sidekiq::Web, at: "/admin/sidekiq", as: :sidekiq mount Sidekiq::Web, at: "/admin/sidekiq", as: :sidekiq
...@@ -121,6 +124,7 @@ Gitlab::Application.routes.draw do ...@@ -121,6 +124,7 @@ Gitlab::Application.routes.draw do
end end
end end
resources :keys resources :keys
resources :emails, only: [:index, :create, :destroy]
resources :groups, only: [:index] do resources :groups, only: [:index] do
member do member do
delete :leave delete :leave
......
class CreateEmails < ActiveRecord::Migration
def change
create_table :emails do |t|
t.integer :user_id, null: false
t.string :email, null: false
t.timestamps
end
add_index :emails, :user_id
add_index :emails, :email, unique: true
end
end
...@@ -127,6 +127,8 @@ But when you want to create a link to web page - use `http:://host/project/issu ...@@ -127,6 +127,8 @@ But when you want to create a link to web page - use `http:://host/project/issu
+ [Projects](projects.md) + [Projects](projects.md)
+ [Project Snippets](project_snippets.md) + [Project Snippets](project_snippets.md)
+ [Repositories](repositories.md) + [Repositories](repositories.md)
+ [Repository Files](repository_files.md)
+ [Commits](commits.md)
+ [Merge Requests](merge_requests.md) + [Merge Requests](merge_requests.md)
+ [Issues](issues.md) + [Issues](issues.md)
+ [Milestones](milestones.md) + [Milestones](milestones.md)
......
# Commits API
## List repository commits
Get a list of repository commits in a project.
```
GET /projects/:id/repository/commits
```
Parameters:
+ `id` (required) - The ID of a project
+ `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch
```json
[
{
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"short_id": "ed899a2f4b5",
"title": "Replace sanitize with escape once",
"author_name": "Dmitriy Zaporozhets",
"author_email": "dzaporozhets@sphereconsultinginc.com",
"created_at": "2012-09-20T11:50:22+03:00"
},
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
"short_id": "6104942438c",
"title": "Sanitize for network graph",
"author_name": "randx",
"author_email": "dmitriy.zaporozhets@gmail.com",
"created_at": "2012-09-20T09:06:12+03:00"
}
]
```
## Get a single commit
Get a specific commit identified by the commit hash or name of a branch or tag.
```
GET /projects/:id/repository/commits/:sha
```
Parameters:
+ `id` (required) - The ID of a project
+ `sha` (required) - The commit hash or name of a repository branch or tag
```json
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
"short_id": "6104942438c",
"title": "Sanitize for network graph",
"author_name": "randx",
"author_email": "dmitriy.zaporozhets@gmail.com",
"created_at": "2012-09-20T09:06:12+03:00",
"committed_date": "2012-09-20T09:06:12+03:00",
"authored_date": "2012-09-20T09:06:12+03:00",
"parent_ids" : [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
]
}
```
## Get the diff of a commit
Get the diff of a commit in a project.
```
GET /projects/:id/repository/commits/:sha/diff
```
Parameters:
+ `id` (required) - The ID of a project
+ `sha` (required) - The name of a repository branch or tag or if not given the default branch
```json
[
{
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"new_path": "doc/update/5.4-to-6.0.md",
"old_path": "doc/update/5.4-to-6.0.md",
"a_mode": null,
"b_mode": "100644",
"new_file": false,
"renamed_file": false,
"deleted_file": false
}
]
```
...@@ -162,7 +162,7 @@ GET /projects/:id/events ...@@ -162,7 +162,7 @@ GET /projects/:id/events
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
```json ```json
...@@ -231,6 +231,7 @@ POST /projects ...@@ -231,6 +231,7 @@ POST /projects
Parameters: Parameters:
+ `name` (required) - new project name + `name` (required) - new project name
+ `namespace_id` (optional) - namespace for the new project (defaults to user)
+ `description` (optional) - short project description + `description` (optional) - short project description
+ `issues_enabled` (optional) + `issues_enabled` (optional)
+ `wall_enabled` (optional) + `wall_enabled` (optional)
...@@ -290,7 +291,7 @@ GET /projects/:id/members ...@@ -290,7 +291,7 @@ GET /projects/:id/members
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `query` (optional) - Query string to search for members + `query` (optional) - Query string to search for members
...@@ -304,7 +305,7 @@ GET /projects/:id/members/:user_id ...@@ -304,7 +305,7 @@ GET /projects/:id/members/:user_id
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `user_id` (required) - The ID of a user + `user_id` (required) - The ID of a user
```json ```json
...@@ -332,7 +333,7 @@ POST /projects/:id/members ...@@ -332,7 +333,7 @@ POST /projects/:id/members
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `user_id` (required) - The ID of a user to add + `user_id` (required) - The ID of a user to add
+ `access_level` (required) - Project access level + `access_level` (required) - Project access level
...@@ -347,7 +348,7 @@ PUT /projects/:id/members/:user_id ...@@ -347,7 +348,7 @@ PUT /projects/:id/members/:user_id
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `user_id` (required) - The ID of a team member + `user_id` (required) - The ID of a team member
+ `access_level` (required) - Project access level + `access_level` (required) - Project access level
...@@ -362,7 +363,7 @@ DELETE /projects/:id/members/:user_id ...@@ -362,7 +363,7 @@ DELETE /projects/:id/members/:user_id
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `user_id` (required) - The ID of a team member + `user_id` (required) - The ID of a team member
This method is idempotent and can be called multiple times with the same parameters. This method is idempotent and can be called multiple times with the same parameters.
...@@ -383,7 +384,7 @@ GET /projects/:id/hooks ...@@ -383,7 +384,7 @@ GET /projects/:id/hooks
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
### Get project hook ### Get project hook
...@@ -396,7 +397,7 @@ GET /projects/:id/hooks/:hook_id ...@@ -396,7 +397,7 @@ GET /projects/:id/hooks/:hook_id
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `hook_id` (required) - The ID of a project hook + `hook_id` (required) - The ID of a project hook
```json ```json
...@@ -422,7 +423,7 @@ POST /projects/:id/hooks ...@@ -422,7 +423,7 @@ POST /projects/:id/hooks
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `url` (required) - The hook URL + `url` (required) - The hook URL
+ `push_events` - Trigger hook on push events + `push_events` - Trigger hook on push events
+ `issues_events` - Trigger hook on issues events + `issues_events` - Trigger hook on issues events
...@@ -439,7 +440,7 @@ PUT /projects/:id/hooks/:hook_id ...@@ -439,7 +440,7 @@ PUT /projects/:id/hooks/:hook_id
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `hook_id` (required) - The ID of a project hook + `hook_id` (required) - The ID of a project hook
+ `url` (required) - The hook URL + `url` (required) - The hook URL
+ `push_events` - Trigger hook on push events + `push_events` - Trigger hook on push events
...@@ -458,7 +459,7 @@ DELETE /projects/:id/hooks/:hook_id ...@@ -458,7 +459,7 @@ DELETE /projects/:id/hooks/:hook_id
Parameters: Parameters:
+ `id` (required) - The ID or NAME of a project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `hook_id` (required) - The ID of hook to delete + `hook_id` (required) - The ID of hook to delete
Note the JSON response differs if the hook is available or not. If the project hook Note the JSON response differs if the hook is available or not. If the project hook
...@@ -477,7 +478,7 @@ GET /projects/:id/repository/branches ...@@ -477,7 +478,7 @@ GET /projects/:id/repository/branches
Parameters: Parameters:
+ `id` (required) - The ID of the project + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
```json ```json
[ [
...@@ -539,7 +540,7 @@ GET /projects/:id/repository/branches/:branch ...@@ -539,7 +540,7 @@ GET /projects/:id/repository/branches/:branch
Parameters: Parameters:
+ `id` (required) - The ID of the project. + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `branch` (required) - The name of the branch. + `branch` (required) - The name of the branch.
...@@ -553,7 +554,7 @@ PUT /projects/:id/repository/branches/:branch/protect ...@@ -553,7 +554,7 @@ PUT /projects/:id/repository/branches/:branch/protect
Parameters: Parameters:
+ `id` (required) - The ID of the project. + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `branch` (required) - The name of the branch. + `branch` (required) - The name of the branch.
...@@ -567,7 +568,7 @@ PUT /projects/:id/repository/branches/:branch/unprotect ...@@ -567,7 +568,7 @@ PUT /projects/:id/repository/branches/:branch/unprotect
Parameters: Parameters:
+ `id` (required) - The ID of the project. + `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
+ `branch` (required) - The name of the branch. + `branch` (required) - The name of the branch.
......
...@@ -204,99 +204,6 @@ Parameters: ...@@ -204,99 +204,6 @@ Parameters:
] ]
``` ```
## List repository commits
Get a list of repository commits in a project.
```
GET /projects/:id/repository/commits
```
Parameters:
+ `id` (required) - The ID of a project
+ `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch
```json
[
{
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"short_id": "ed899a2f4b5",
"title": "Replace sanitize with escape once",
"author_name": "Dmitriy Zaporozhets",
"author_email": "dzaporozhets@sphereconsultinginc.com",
"created_at": "2012-09-20T11:50:22+03:00"
},
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
"short_id": "6104942438c",
"title": "Sanitize for network graph",
"author_name": "randx",
"author_email": "dmitriy.zaporozhets@gmail.com",
"created_at": "2012-09-20T09:06:12+03:00"
}
]
```
## Get a single commit
Get a specific commit identified by the commit hash or name of a branch or tag.
```
GET /projects/:id/repository/commits/:sha
```
Parameters:
+ `id` (required) - The ID of a project
+ `sha` (required) - The commit hash or name of a repository branch or tag
```json
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
"short_id": "6104942438c",
"title": "Sanitize for network graph",
"author_name": "randx",
"author_email": "dmitriy.zaporozhets@gmail.com",
"created_at": "2012-09-20T09:06:12+03:00",
"committed_date": "2012-09-20T09:06:12+03:00",
"authored_date": "2012-09-20T09:06:12+03:00",
"parent_ids" : [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
]
}
```
## Get the diff of a commit
Get the diff of a commit in a project.
```
GET /projects/:id/repository/commits/:sha/diff
```
Parameters:
+ `id` (required) - The ID of a project
+ `sha` (required) - The name of a repository branch or tag or if not given the default branch
```json
[
{
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"new_path": "doc/update/5.4-to-6.0.md",
"old_path": "doc/update/5.4-to-6.0.md",
"a_mode": null,
"b_mode": "100644",
"new_file": false,
"renamed_file": false,
"deleted_file": false
}
]
```
## List repository tree ## List repository tree
Get a list of repository files and directories in a project. Get a list of repository files and directories in a project.
...@@ -388,44 +295,3 @@ GET /projects/:id/repository/archive ...@@ -388,44 +295,3 @@ GET /projects/:id/repository/archive
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch + `sha` (optional) - The commit sha to download defaults to the tip of the default branch
## Create new file in repository
```
POST /projects/:id/repository/files
```
Parameters:
+ `file_path` (optional) - Full path to new file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `encoding` (optional) - 'text' or 'base64'. Text is default.
+ `content` (required) - File content
+ `commit_message` (required) - Commit message
## Update existing file in repository
```
PUT /projects/:id/repository/files
```
Parameters:
+ `file_path` (required) - Full path to file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `encoding` (optional) - 'text' or 'base64'. Text is default.
+ `content` (required) - New file content
+ `commit_message` (required) - Commit message
## Delete existing file in repository
```
DELETE /projects/:id/repository/files
```
Parameters:
+ `file_path` (required) - Full path to file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `commit_message` (required) - Commit message
# CRUD for repository files
## Create, read, update and delete repository files using this API
- - -
## Get file from repository
Allows you to receive information about file in repository like name, size, content.
Note that file content is Base64 encoded.
```
GET /projects/:id/repository/files
```
Example response:
```json
{
"file_name": "key.rb",
"file_path": "app/models/key.rb",
"size": 1476,
"encoding": "base64",
"content": "IyA9PSBTY2hlbWEgSW5mb3...",
"ref": "master",
"blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
"commit_id": "d5a3ff139356ce33e37e73add446f16869741b50"
}
```
Parameters:
+ `file_path` (required) - Full path to new file. Ex. lib/class.rb
+ `ref` (required) - The name of branch, tag or commit
## Create new file in repository
```
POST /projects/:id/repository/files
```
Example response:
```json
{
"file_name": "app/project.rb",
"branch_name": "master",
}
```
Parameters:
+ `file_path` (required) - Full path to new file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `encoding` (optional) - 'text' or 'base64'. Text is default.
+ `content` (required) - File content
+ `commit_message` (required) - Commit message
## Update existing file in repository
```
PUT /projects/:id/repository/files
```
Example response:
```json
{
"file_name": "app/project.rb",
"branch_name": "master",
}
```
Parameters:
+ `file_path` (required) - Full path to file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `encoding` (optional) - 'text' or 'base64'. Text is default.
+ `content` (required) - New file content
+ `commit_message` (required) - Commit message
## Delete existing file in repository
```
DELETE /projects/:id/repository/files
```
Example response:
```json
{
"file_name": "app/project.rb",
"branch_name": "master",
}
```
Parameters:
+ `file_path` (required) - Full path to file. Ex. lib/class.rb
+ `branch_name` (required) - The name of branch
+ `commit_message` (required) - Commit message
...@@ -56,9 +56,6 @@ Install the required packages: ...@@ -56,9 +56,6 @@ Install the required packages:
sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate
# For reStructuredText markup language support install required package:
sudo apt-get install -y python-docutils
Make sure you have the right version of Git installed Make sure you have the right version of Git installed
# Install Git # Install Git
...@@ -147,7 +144,7 @@ GitLab Shell is an ssh access and repository management software developed speci ...@@ -147,7 +144,7 @@ GitLab Shell is an ssh access and repository management software developed speci
# 5. Database # 5. Database
To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md). To setup the MySQL/PostgreSQL database and dependencies please see [doc/install/databases.md](./databases.md).
# 6. GitLab # 6. GitLab
...@@ -353,6 +350,15 @@ nobody can access your GitLab by using this login information later on. ...@@ -353,6 +350,15 @@ nobody can access your GitLab by using this login information later on.
# Advanced Setup Tips # Advanced Setup Tips
## Additional markup styles
Apart from the always supported markdown style there are other rich text files that GitLab can display.
But you might have to install a depency to do so.
Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
For example, reStructuredText markup language support requires python-docutils:
sudo apt-get install -y python-docutils
## Custom Redis Connection ## Custom Redis Connection
If you'd like Resque to connect to a Redis server on a non-standard port or on If you'd like Resque to connect to a Redis server on a non-standard port or on
......
...@@ -5,36 +5,32 @@ Table of Contents ...@@ -5,36 +5,32 @@ Table of Contents
---------------------------------------------- ----------------------------------------------
[GitLab Flavored Markdown](#toc_3) **[GitLab Flavored Markdown](#gitlab-flavored-markdown-gfm)**
-------------------------------
[Newlines](#toc_4) [Newlines](#newlines)
[Multiple underscores in words](#toc_5) [Multiple underscores in words](#multiple-underscores-in-words)
[URL autolinking](#toc_6) [URL autolinking](#url-autolinking)
[Code and Syntax Highlighting](#toc_7) [Code and Syntax Highlighting](#code-and-syntax-highlighting)
[Emoji](#toc_8) [Emoji](#emoji)
[Special GitLab references](#toc_9) [Special GitLab references](#special-gitlab-references)
**[Standard Markdown](#standard-markdown)**
[Standard Markdown](#toc_10) [Headers](#headers)
------------------------------ [Emphasis](#emphasis)
[Headers](#toc_11) [Lists](#lists)
[Emphasis](#toc_20) [Links](#links)
[Lists](#toc_21) [Images](#images)
[Links](#toc_22) [Blockquotes](#blockquotes)
[Images](#toc_23) [Inline HTML](#inline-html)
[Blockquotes](#toc_24) [Horizontal Rule](#horizontal-rule)
[Inline HTML](#toc_25) [Line Breaks](#line-breaks)
[Horizontal Rule](#toc_26) [Tables](#tables)
[Line Breaks](#toc_27)
[Tables](#toc_28) **[References](#references)**
[References](#toc_29)
---------------------
---------------------------------------------- ----------------------------------------------
<a name="gfm" />
GitLab Flavored Markdown (GFM) GitLab Flavored Markdown (GFM)
============================== ==============================
For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality.
...@@ -49,7 +45,10 @@ You can use GFM in ...@@ -49,7 +45,10 @@ You can use GFM in
* milestones * milestones
* wiki pages * wiki pages
<a name="newlines" /> You can also use other rich text files in GitLab.
You might have to install a depency to do so.
Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
Newlines Newlines
-------- --------
The biggest difference that GFM introduces is in the handling of linebreaks. With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended. The biggest difference that GFM introduces is in the handling of linebreaks. With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended.
...@@ -62,7 +61,6 @@ The next paragraph contains two phrases separated by a single newline character: ...@@ -62,7 +61,6 @@ The next paragraph contains two phrases separated by a single newline character:
Roses are red Roses are red
Violets are blue Violets are blue
<a name="underscores" />
Multiple underscores in words Multiple underscores in words
----------------------------- -----------------------------
It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words. It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words.
...@@ -73,7 +71,6 @@ It is not reasonable to italicize just _part_ of a word, especially when you're ...@@ -73,7 +71,6 @@ It is not reasonable to italicize just _part_ of a word, especially when you're
perform_complicated_task perform_complicated_task
do_this_and_do_that_and_another_thing do_this_and_do_that_and_another_thing
<a name="autolink" />
URL autolinking URL autolinking
--------------- ---------------
GFM will autolink standard URLs you copy and paste into your text. GFM will autolink standard URLs you copy and paste into your text.
...@@ -83,12 +80,10 @@ So if you want to link to a URL (instead of a textural link), you can simply put ...@@ -83,12 +80,10 @@ So if you want to link to a URL (instead of a textural link), you can simply put
http://www.google.com http://www.google.com
<a name="code"/>
## Code and Syntax Highlighting ## Code and Syntax Highlighting
Blocks of code are either fenced by lines with three back-ticks <code>```</code>, or are indented with four spaces. Only the fenced code blocks support syntax highlighting. Blocks of code are either fenced by lines with three back-ticks <code>```</code>, or are indented with four spaces. Only the fenced code blocks support syntax highlighting.
```no-highlight ```no-highlight
Inline `code` has `back-ticks around` it. Inline `code` has `back-ticks around` it.
``` ```
...@@ -147,7 +142,6 @@ s = "There is no highlighting for this." ...@@ -147,7 +142,6 @@ s = "There is no highlighting for this."
But let's throw in a <b>tag</b>. But let's throw in a <b>tag</b>.
``` ```
<a name="emoji"/>
Emoji Emoji
----- -----
...@@ -171,7 +165,6 @@ If you are :new: to this, don't be :fearful:. You can easily join the emoji :cir ...@@ -171,7 +165,6 @@ If you are :new: to this, don't be :fearful:. You can easily join the emoji :cir
Consult the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for a list of all supported emoji codes. :thumbsup: Consult the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com/) for a list of all supported emoji codes. :thumbsup:
<a name="special"/>
Special GitLab References Special GitLab References
----- -----
...@@ -179,7 +172,6 @@ GFM recognized special references. ...@@ -179,7 +172,6 @@ GFM recognized special references.
You can easily reference e.g. a team member, an issue, or a commit within a project. You can easily reference e.g. a team member, an issue, or a commit within a project.
GFM will turn that reference into a link so you can navigate between them easily. GFM will turn that reference into a link so you can navigate between them easily.
GFM will recognize the following: GFM will recognize the following:
* @foo : for team members * @foo : for team members
...@@ -189,13 +181,10 @@ GFM will recognize the following: ...@@ -189,13 +181,10 @@ GFM will recognize the following:
* 1234567 : for commits * 1234567 : for commits
* \[file\](path/to/file) : for file references * \[file\](path/to/file) : for file references
<a name="standard"/>
---------------------------------- ----------------------------------
# Standard Markdown # Standard Markdown
---------------------------------- ----------------------------------
<a name="headers"/>
## Headers ## Headers
```no-highlight ```no-highlight
...@@ -230,7 +219,54 @@ Alt-H1 ...@@ -230,7 +219,54 @@ Alt-H1
Alt-H2 Alt-H2
------ ------
<a name="emphasis"/> ### Header IDs and links
All markdown rendered headers automatically get IDs, except for comments.
On hover a link to those IDs becomes visible to make it easier to copy the link to the header to give it to someone else.
The IDs are generated from the content of the header according to the following rules:
1) remove the heading hashes `#` and process the rest of the line as it would be processed if it were not a header
2) from the result, remove all HTML tags, but keep their inner content
3) convert all characters to lowercase
4) convert all characters except `[a-z0-9_-]` into hyphens `-`
5) transform multiple adjacent hyphens into a single hyphen
6) remove trailing and heading hyphens
For example:
```
###### ..Ab_c-d. e [anchor](url) ![alt text](url)..
```
which renders as:
###### ..Ab_c-d. e [anchor](url) ![alt text](url)..
will first be converted by step 1) into a string like:
```
..Ab_c-d. e &lt;a href="url">anchor&lt;/a> &lt;img src="url" alt="alt text"/>..
```
After removing the tags in step 2) we get:
```
..Ab_c-d. e anchor ..
```
And applying all the other steps gives the id:
```
ab_c-d-e-anchor
```
Note in particular how:
- for markdown anchors `[text](url)`, only the `text` is used
- markdown images `![alt](url)` are completely ignored
## Emphasis ## Emphasis
```no-highlight ```no-highlight
...@@ -251,8 +287,6 @@ Combined emphasis with **asterisks and _underscores_**. ...@@ -251,8 +287,6 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~ Strikethrough uses two tildes. ~~Scratch this.~~
<a name="lists"/>
## Lists ## Lists
```no-highlight ```no-highlight
...@@ -283,7 +317,6 @@ Strikethrough uses two tildes. ~~Scratch this.~~ ...@@ -283,7 +317,6 @@ Strikethrough uses two tildes. ~~Scratch this.~~
- Or minuses - Or minuses
+ Or pluses + Or pluses
<a name="links"/>
## Links ## Links
There are two ways to create links. There are two ways to create links.
...@@ -320,7 +353,6 @@ Some text to show that the reference links can follow later. ...@@ -320,7 +353,6 @@ Some text to show that the reference links can follow later.
[1]: http://slashdot.org [1]: http://slashdot.org
[link text itself]: http://www.reddit.com [link text itself]: http://www.reddit.com
<a name="images"/>
## Images ## Images
Here's our logo (hover to see the title text): Here's our logo (hover to see the title text):
...@@ -343,7 +375,6 @@ Reference-style: ...@@ -343,7 +375,6 @@ Reference-style:
[logo]: /assets/logo-white.png "Logo Title Text 2" [logo]: /assets/logo-white.png "Logo Title Text 2"
<a name="blockquotes"/>
## Blockquotes ## Blockquotes
```no-highlight ```no-highlight
...@@ -362,7 +393,6 @@ Quote break. ...@@ -362,7 +393,6 @@ Quote break.
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote. > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
<a name="html"/>
## Inline HTML ## Inline HTML
You can also use raw HTML in your Markdown, and it'll mostly work pretty well. You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
...@@ -385,7 +415,6 @@ You can also use raw HTML in your Markdown, and it'll mostly work pretty well. ...@@ -385,7 +415,6 @@ You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
<dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd> <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
</dl> </dl>
<a name="hr"/>
## Horizontal Rule ## Horizontal Rule
``` ```
...@@ -418,7 +447,6 @@ ___ ...@@ -418,7 +447,6 @@ ___
Underscores Underscores
<a name="lines"/>
## Line Breaks ## Line Breaks
My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend. My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend.
...@@ -441,8 +469,6 @@ This line is separated from the one above by two newlines, so it will be a *sepa ...@@ -441,8 +469,6 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also begins a separate paragraph, but... This line is also begins a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*. This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
<a name="tables"/>
## Tables ## Tables
Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them. Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them.
...@@ -461,10 +487,8 @@ Code above produces next output: ...@@ -461,10 +487,8 @@ Code above produces next output:
| cell 1 | cell 2 | | cell 1 | cell 2 |
| cell 3 | cell 4 | | cell 3 | cell 4 |
------------ ------------
<a name="references"/>
## References ## References
* This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). * This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
......
...@@ -14,3 +14,19 @@ Notes: ...@@ -14,3 +14,19 @@ Notes:
```bash ```bash
bundle exec rake gitlab:import:all_users_to_all_projects bundle exec rake gitlab:import:all_users_to_all_projects
``` ```
### Add user as a developer to all projects
```
bundle exec rake gitlab:import:user_to_groups[username@domain.tld]
```
### Add all users to all groups
Notes:
* admin users are added as owners so they can add additional users to the group
```
bundle exec rake gitlab:import:all_users_to_all_groups
```
...@@ -61,7 +61,7 @@ After making the release branch new commits are cherry-picked from master. When ...@@ -61,7 +61,7 @@ After making the release branch new commits are cherry-picked from master. When
* 17th: feature freeze (stop merging new features in master) * 17th: feature freeze (stop merging new features in master)
* 18th: UI freeze (stop merging changes to the user interface) * 18th: UI freeze (stop merging changes to the user interface)
* 19th: code freeze (stop merging non-essential code improvements) * 19th: code freeze (stop merging non-essential code improvements)
* 20th: release candidate 1 (VERSION x.x.0.pre, tag and tweet about x.x.0.rc1) * 20th: release candidate 1 (VERSION x.x.0.rc1, tag and tweet about x.x.0.rc1)
* 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems) * 21st: optional release candidate 2 (x.x.0.rc2, only if rc1 had problems)
* 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet) * 22nd: release (VERSION x.x.0, create x-x-stable branch, tag, blog and tweet)
* 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems) * 23nd: optional patch releases (x.x.1, x.x.2, etc., only if there are serious problems)
......
...@@ -91,7 +91,7 @@ Note: We switched from Puma in GitLab 5.4 to unicorn in GitLab 6.0. ...@@ -91,7 +91,7 @@ Note: We switched from Puma in GitLab 5.4 to unicorn in GitLab 6.0.
```bash ```bash
sudo rm /etc/init.d/gitlab sudo rm /etc/init.d/gitlab
sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-0-stable/lib/support/init.d/gitlab sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab sudo chmod +x /etc/init.d/gitlab
``` ```
......
...@@ -80,7 +80,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production ...@@ -80,7 +80,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```bash ```bash
sudo rm /etc/init.d/gitlab sudo rm /etc/init.d/gitlab
sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/6-1-stable/lib/support/init.d/gitlab sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab sudo chmod +x /etc/init.d/gitlab
``` ```
......
...@@ -9,7 +9,9 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ...@@ -9,7 +9,9 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
### 1. Stop server ### 1. Stop server
sudo service gitlab stop ```bash
sudo service gitlab stop
````
### 2. Get latest code ### 2. Get latest code
...@@ -61,18 +63,24 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS ...@@ -61,18 +63,24 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
### 5. Start application ### 5. Start application
sudo service gitlab start ```bash
sudo service nginx restart sudo service gitlab start
sudo service nginx restart
```
### 8. Check application status ### 6. Check application status
Check if GitLab and its environment are configured correctly: Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production ```bash
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
```
To make sure you didn't miss anything run a more thorough check with: To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production ```bash
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
If all items are green, then congratulations upgrade complete! If all items are green, then congratulations upgrade complete!
......
...@@ -77,7 +77,7 @@ To make sure you didn't miss anything run a more thorough check with: ...@@ -77,7 +77,7 @@ To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade complete! If all items are green, then congratulations upgrade is complete!
## Things went south? Revert to previous version (6.4) ## Things went south? Revert to previous version (6.4)
...@@ -91,3 +91,4 @@ Follow the [`upgrade guide from 6.3 to 6.4`](6.3-to-6.4.md), except for the data ...@@ -91,3 +91,4 @@ Follow the [`upgrade guide from 6.3 to 6.4`](6.3-to-6.4.md), except for the data
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
``` ```
If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
...@@ -21,10 +21,22 @@ __GitLab Upgrader is available only for GitLab version 6.4.2 or higher__ ...@@ -21,10 +21,22 @@ __GitLab Upgrader is available only for GitLab version 6.4.2 or higher__
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H ruby script/upgrade.rb sudo -u git -H ruby script/upgrade.rb
# it also supports -y option to avoid waiting for user input # to perform a non-interactive install (no user input required) you can add -y
# sudo -u git -H ruby script/upgrade.rb -y # sudo -u git -H ruby script/upgrade.rb -y
### 3. Start application ### 3. Start application
sudo service gitlab start sudo service gitlab start
sudo service nginx restart sudo service nginx restart
### 4. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations upgrade is complete!
...@@ -2,7 +2,7 @@ Feature: Admin Groups ...@@ -2,7 +2,7 @@ Feature: Admin Groups
Background: Background:
Given I sign in as an admin Given I sign in as an admin
And I have group with projects And I have group with projects
And Create gitlab user "John" And User "John Doe" exists
And I visit admin groups page And I visit admin groups page
Scenario: See group list Scenario: See group list
...@@ -17,5 +17,5 @@ Feature: Admin Groups ...@@ -17,5 +17,5 @@ Feature: Admin Groups
@javascript @javascript
Scenario: Add user into projects in group Scenario: Add user into projects in group
When I visit admin group page When I visit admin group page
When I select user "John" from user list as "Reporter" When I select user "John Doe" from user list as "Reporter"
Then I should see "John" in team list in every project as "Reporter" Then I should see "John Doe" in team list in every project as "Reporter"
Feature: Help
Background:
Given I sign in as a user
And I visit the "Rake Tasks" help page
Scenario: The markdown should be rendered correctly
Then I should see "Rake Tasks" page markdown rendered
And Header "Rebuild project satellites" should have correct ids and links
Feature: Groups
Background:
Given I sign in as "John Doe"
And "John Doe" is owner of group "Owned"
And "John Doe" is guest of group "Guest"
@javascript
Scenario: I should see group "Owned" dashboard list
When I visit group "Owned" page
Then I should see group "Owned" projects list
And I should see projects activity feed
Scenario: Create a group from dasboard
When I visit group "Owned" page
And I visit dashboard page
And I click new group link
And submit form with new group "Samurai" info
Then I should be redirected to group "Samurai" page
And I should see newly created group "Samurai"
Scenario: I should see group "Owned" issues list
Given project from group "Owned" has issues assigned to me
When I visit group "Owned" issues page
Then I should see issues from group "Owned" assigned to me
Scenario: I should see group "Owned" merge requests list
Given project from group "Owned" has merge requests assigned to me
When I visit group "Owned" merge requests page
Then I should see merge requests from group "Owned" assigned to me
@javascript
Scenario: I should add user to projects in group "Owned"
Given User "Mary Jane" exists
When I visit group "Owned" members page
And I select user "Mary Jane" from list with role "Reporter"
Then I should see user "Mary Jane" in team list
Scenario: I should see edit group "Owned" page
When I visit group "Owned" settings page
And I change group "Owned" name to "new-name"
Then I should see new group "Owned" name
Scenario: I edit group "Owned" avatar
When I visit group "Owned" settings page
And I change group "Owned" avatar
And I visit group "Owned" settings page
Then I should see new group "Owned" avatar
And I should see the "Remove avatar" button
Scenario: I remove group "Owned" avatar
When I visit group "Owned" settings page
And I have group "Owned" avatar
And I visit group "Owned" settings page
And I remove group "Owned" avatar
Then I should not see group "Owned" avatar
And I should not see the "Remove avatar" button
# Leave
@javascript
Scenario: Owner should be able to remove himself from group if he is not the last owner
Given "Mary Jane" is owner of group "Owned"
When I visit group "Owned" members page
Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
When I click on the "Remove User From Group" button for "John Doe"
And I visit group "Owned" members page
Then I should not see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
@javascript
Scenario: Owner should not be able to remove himself from group if he is the last owner
Given "Mary Jane" is guest of group "Owned"
When I visit group "Owned" members page
Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
Then I should not see the "Remove User From Group" button for "Mary Jane"
@javascript
Scenario: Guest should be able to remove himself from group
Given "Mary Jane" is guest of group "Guest"
When I visit group "Guest" members page
Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
When I click on the "Remove User From Group" button for "John Doe"
When I visit group "Guest" members page
Then I should not see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
@javascript
Scenario: Guest should be able to remove himself from group even if he is the only user in the group
When I visit group "Guest" members page
Then I should see user "John Doe" in team list
When I click on the "Remove User From Group" button for "John Doe"
When I visit group "Guest" members page
Then I should not see user "John Doe" in team list
# Remove others
@javascript
Scenario: Owner should be able to remove other users from group
Given "Mary Jane" is owner of group "Owned"
When I visit group "Owned" members page
Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
When I click on the "Remove User From Group" button for "Mary Jane"
When I visit group "Owned" members page
Then I should see user "John Doe" in team list
Then I should not see user "Mary Jane" in team list
Scenario: Guest should not be able to remove other users from group
Given "Mary Jane" is guest of group "Guest"
When I visit group "Guest" members page
Then I should see user "John Doe" in team list
Then I should see user "Mary Jane" in team list
Then I should not see the "Remove User From Group" button for "Mary Jane"
Feature: Groups
Background:
Given I sign in as a user
Scenario: Create a group from dasboard
Given I have group with projects
And I visit dashboard page
When I click new group link
And submit form with new group info
Then I should be redirected to group page
And I should see newly created group
Feature: Groups
Background:
Given I sign in as a user
And I have group with projects
@javascript
Scenario: I should see group dashboard list
When I visit group page
Then I should see projects list
And I should see projects activity feed
Scenario: I should see group issues list
Given project from group has issues assigned to me
When I visit group issues page
Then I should see issues from this group assigned to me
Scenario: I should see group merge requests list
Given project from group has merge requests assigned to me
When I visit group merge requests page
Then I should see merge requests from this group assigned to me
@javascript
Scenario: I should add user to projects in Group
Given I have new user "John"
When I visit group members page
And I select user "John" from list with role "Reporter"
Then I should see user "John" in team list
Scenario: I should see edit group page
When I visit group settings page
And I change group name
Then I should see new group name
Scenario: I edit my group avatar
When I visit group settings page
And I change my group avatar
And I visit group settings page
Then I should see new group avatar
And I should see the "Remove avatar" button
Scenario: I remove my group avatar
When I visit group settings page
And I have an group avatar
And I visit group settings page
And I remove my group avatar
Then I should not see my group avatar
And I should not see the "Remove avatar" button
Feature: Profile Emails
Background:
Given I sign in as a user
And I visit profile emails page
Scenario: I should see emails
Then I should see my emails
Scenario: Add new email
Given I submit new email "my@email.com"
Then I should see new email "my@email.com"
And I should see my emails
Scenario: Add duplicate email
Given I submit duplicate email @user.email
Then I should not have @user.email added
And I should see my emails
Scenario: Remove email
Given I submit new email "my@email.com"
Then I should see new email "my@email.com"
And I should see my emails
Then I click link "Remove" for "my@email.com"
Then I should not see email "my@email.com"
And I should see my emails
Feature: Profile Group
Background:
Given I sign in as "John Doe"
And "John Doe" is owner of group "Owned"
And "John Doe" is guest of group "Guest"
# Leave groups
@javascript
Scenario: Owner should be able to leave from group if he is not the last owner
Given "Mary Jane" is owner of group "Owned"
When I visit profile groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Owned"
And I visit profile groups page
Then I should not see group "Owned" in group list
Then I should see group "Guest" in group list
@javascript
Scenario: Owner should not be able to leave from group if he is the last owner
Given "Mary Jane" is guest of group "Owned"
When I visit profile groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
Then I should not see the "Leave" button for group "Owned"
@javascript
Scenario: Guest should be able to leave from group
Given "Mary Jane" is guest of group "Guest"
When I visit profile groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Guest"
When I visit profile groups page
Then I should see group "Owned" in group list
Then I should not see group "Guest" in group list
@javascript
Scenario: Guest should be able to leave from group even if he is the only user in the group
When I visit profile groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Guest"
When I visit profile groups page
Then I should see group "Owned" in group list
Then I should not see group "Guest" in group list
Feature: Project Browse Commits User Lookup
Background:
Given I sign in as a user
And I own a project
And I have the user that authored the commits
And I visit my project's commits page
Scenario: I browse commit from list
Given I click on commit link
Then I see commit info
Scenario: I browse another commit from list
Given I click on another commit link
Then I see other commit info
\ No newline at end of file
Feature: Project Filter Labels
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" has issue "Bugfix1" with tags: "bug", "feature"
And project "Shop" has issue "Bugfix2" with tags: "bug", "enhancement"
And project "Shop" has issue "Feature1" with tags: "feature"
Given I visit project "Shop" issues page
Scenario: I should see project issues
Then I should see "bug" in labels filter
And I should see "feature" in labels filter
And I should see "enhancement" in labels filter
Scenario: I filter by one label
Given I click link "bug"
Then I should see "Bugfix1" in issues list
And I should see "Bugfix2" in issues list
And I should not see "Feature1" in issues list
Scenario: I filter by two labels
Given I click link "bug"
And I click link "feature"
Then I should see "Bugfix1" in issues list
And I should not see "Bugfix2" in issues list
And I should not see "Feature1" in issues list
...@@ -55,3 +55,15 @@ Feature: Project Issues ...@@ -55,3 +55,15 @@ Feature: Project Issues
And I fill in issue search with ".3" And I fill in issue search with ".3"
Then I should see "Release 0.3" in issues Then I should see "Release 0.3" in issues
And I should not see "Release 0.4" in issues And I should not see "Release 0.4" in issues
# Markdown
Scenario: Headers inside the description should have ids generated for them.
Given I visit issue page "Release 0.4"
Then Header "Description header" should have correct id and link
@javascript
Scenario: Headers inside comments should not have ids generated for them.
Given I visit issue page "Release 0.4"
And I leave a comment with a header containing "Comment with a header"
Then The comment with the header should not have an ID
...@@ -22,3 +22,9 @@ Feature: Project Milestones ...@@ -22,3 +22,9 @@ Feature: Project Milestones
Given the milestone has open and closed issues Given the milestone has open and closed issues
And I click link "v2.2" And I click link "v2.2"
Then I should see 3 issues Then I should see 3 issues
# Markdown
Scenario: Headers inside the description should have ids generated for them.
Given I click link "v2.2"
Then Header "Description header" should have correct id and link
...@@ -29,7 +29,6 @@ Feature: Project Merge Requests ...@@ -29,7 +29,6 @@ Feature: Project Merge Requests
And I click link "Close" And I click link "Close"
Then I should see closed merge request "Bug NS-04" Then I should see closed merge request "Bug NS-04"
@javascript
Scenario: I submit new unassigned merge request Scenario: I submit new unassigned merge request
Given I click link "New Merge Request" Given I click link "New Merge Request"
And I submit new merge request "Wiki Feature" And I submit new merge request "Wiki Feature"
...@@ -77,3 +76,15 @@ Feature: Project Merge Requests ...@@ -77,3 +76,15 @@ Feature: Project Merge Requests
Then I modify merge commit message Then I modify merge commit message
And I accept this merge request And I accept this merge request
Then I should see merged request Then I should see merged request
# Markdown
Scenario: Headers inside the description should have ids generated for them.
When I visit merge request page "Bug NS-04"
Then Header "Description header" should have correct id and link
@javascript
Scenario: Headers inside comments should not have ids generated for them.
Given I visit merge request page "Bug NS-04"
And I leave a comment with a header containing "Comment with a header"
Then The comment with the header should not have an ID
...@@ -4,6 +4,15 @@ Feature: Project markdown render ...@@ -4,6 +4,15 @@ Feature: Project markdown render
And I own project "Delta" And I own project "Delta"
Given I visit project source page Given I visit project source page
# -------------------------------------------
# README
# -------------------------------------------
Scenario: Tree view should have correct links in README
Given I go directory which contains README file
And I click on a relative link in README
Then I should see the correct markdown
Scenario: I browse files from master branch Scenario: I browse files from master branch
Then I should see files from repository in master Then I should see files from repository in master
And I should see rendered README which contains correct links And I should see rendered README which contains correct links
...@@ -28,6 +37,14 @@ Feature: Project markdown render ...@@ -28,6 +37,14 @@ Feature: Project markdown render
And I click on Maintenance in README And I click on Maintenance in README
Then I should see correct maintenance file rendered Then I should see correct maintenance file rendered
Scenario: README headers should have header links
Then I should see rendered README which contains correct links
And Header "Application details" should have correct id and link
# -------------------------------------------
# File content
# -------------------------------------------
Scenario: I navigate to doc directory to view documentation in master Scenario: I navigate to doc directory to view documentation in master
And I navigate to the doc/api/README And I navigate to the doc/api/README
And I see correct file rendered And I see correct file rendered
...@@ -40,6 +57,14 @@ Feature: Project markdown render ...@@ -40,6 +57,14 @@ Feature: Project markdown render
And I click on raketasks in doc/api/README And I click on raketasks in doc/api/README
Then I should see correct directory rendered Then I should see correct directory rendered
Scenario: I navigate to doc directory to view user doc in master
And I navigate to the doc/api/README
And Header "GitLab API" should have correct id and link
# -------------------------------------------
# Markdown branch README
# -------------------------------------------
Scenario: I browse files from markdown branch Scenario: I browse files from markdown branch
When I visit markdown branch When I visit markdown branch
Then I should see files from repository in markdown branch Then I should see files from repository in markdown branch
...@@ -68,6 +93,10 @@ Feature: Project markdown render ...@@ -68,6 +93,10 @@ Feature: Project markdown render
And I click on raketasks in doc/api/README And I click on raketasks in doc/api/README
Then I should see correct directory rendered for markdown branch Then I should see correct directory rendered for markdown branch
# -------------------------------------------
# Wiki
# -------------------------------------------
Scenario: I create a wiki page with different links Scenario: I create a wiki page with different links
Given I go to wiki page Given I go to wiki page
And I add various links to the wiki page And I add various links to the wiki page
...@@ -81,12 +110,7 @@ Feature: Project markdown render ...@@ -81,12 +110,7 @@ Feature: Project markdown render
And I click on Rake tasks link And I click on Rake tasks link
Then I see Rake tasks directory Then I see Rake tasks directory
Scenario: I visit the help page with markdown Scenario: Wiki headers should have should have ids generated for them.
Given I visit to the help page Given I go to wiki page
And I select a page with markdown And I add a header to the wiki page
Then I should see a help page with markdown Then Wiki header should have correct id and link
Scenario: Tree view should have correct links in README
Given I go directory which contains README file
And I click on a relative link in README
Then I should see the correct markdown
class AdminGroups < Spinach::FeatureSteps class AdminGroups < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include SharedUser
include SharedActiveTab include SharedActiveTab
include Select2Helper include Select2Helper
...@@ -20,10 +21,6 @@ class AdminGroups < Spinach::FeatureSteps ...@@ -20,10 +21,6 @@ class AdminGroups < Spinach::FeatureSteps
@project.team << [current_user, :master] @project.team << [current_user, :master]
end end
And 'Create gitlab user "John"' do
create(:user, name: "John")
end
And 'submit form with new group info' do And 'submit form with new group info' do
fill_in 'group_name', with: 'gitlab' fill_in 'group_name', with: 'gitlab'
fill_in 'group_description', with: 'Group description' fill_in 'group_description', with: 'Group description'
...@@ -39,8 +36,8 @@ class AdminGroups < Spinach::FeatureSteps ...@@ -39,8 +36,8 @@ class AdminGroups < Spinach::FeatureSteps
current_path.should == admin_group_path(Group.last) current_path.should == admin_group_path(Group.last)
end end
When 'I select user "John" from user list as "Reporter"' do When 'I select user "John Doe" from user list as "Reporter"' do
user = User.find_by(name: "John") user = User.find_by(name: "John Doe")
select2(user.id, from: "#user_ids", multiple: true) select2(user.id, from: "#user_ids", multiple: true)
within "#new_team_member" do within "#new_team_member" do
select "Reporter", from: "group_access" select "Reporter", from: "group_access"
...@@ -48,9 +45,9 @@ class AdminGroups < Spinach::FeatureSteps ...@@ -48,9 +45,9 @@ class AdminGroups < Spinach::FeatureSteps
click_button "Add users into group" click_button "Add users into group"
end end
Then 'I should see "John" in team list in every project as "Reporter"' do Then 'I should see "John Doe" in team list in every project as "Reporter"' do
within ".group-users-list" do within ".group-users-list" do
page.should have_content "John" page.should have_content "John Doe"
page.should have_content "Reporter" page.should have_content "Reporter"
end end
end end
......
class Groups < Spinach::FeatureSteps class Groups < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include SharedGroup
include SharedUser
include Select2Helper include Select2Helper
Then 'I should see projects list' do Then 'I should see group "Owned" projects list' do
current_user.authorized_projects.each do |project| Group.find_by(name: "Owned").projects.each do |project|
page.should have_link project.name page.should have_link project.name
end end
end end
And 'I have group with projects' do
@group = create(:group)
@group.add_owner(current_user)
@project = create(:project, namespace: @group)
@event = create(:closed_issue_event, project: @project)
@project.team << [current_user, :master]
end
And 'I should see projects activity feed' do And 'I should see projects activity feed' do
page.should have_content 'closed issue' page.should have_content 'closed issue'
end end
Then 'I should see issues from this group assigned to me' do Then 'I should see issues from group "Owned" assigned to me' do
assigned_to_me(:issues).each do |issue| assigned_to_me(:issues).each do |issue|
page.should have_content issue.title page.should have_content issue.title
end end
end end
Then 'I should see merge requests from this group assigned to me' do Then 'I should see merge requests from group "Owned" assigned to me' do
assigned_to_me(:merge_requests).each do |issue| assigned_to_me(:merge_requests).each do |issue|
page.should have_content issue.title[0..80] page.should have_content issue.title[0..80]
end end
end end
Given 'I have new user "John"' do And 'I select user "Mary Jane" from list with role "Reporter"' do
create(:user, name: "John") user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
end click_link 'Add members'
And 'I select user "John" from list with role "Reporter"' do
user = User.find_by(name: "John")
within ".users-group-form" do within ".users-group-form" do
select2(user.id, from: "#user_ids", multiple: true) select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "group_access" select "Reporter", from: "group_access"
...@@ -47,19 +37,34 @@ class Groups < Spinach::FeatureSteps ...@@ -47,19 +37,34 @@ class Groups < Spinach::FeatureSteps
click_button "Add users into group" click_button "Add users into group"
end end
Then 'I should see user "John" in team list' do Then 'I should see user "John Doe" in team list' do
projects_with_access = find(".ui-box .well-list")
projects_with_access.should have_content("John Doe")
end
Then 'I should not see user "John Doe" in team list' do
projects_with_access = find(".ui-box .well-list") projects_with_access = find(".ui-box .well-list")
projects_with_access.should have_content("John") projects_with_access.should_not have_content("John Doe")
end end
Given 'project from group has issues assigned to me' do Then 'I should see user "Mary Jane" in team list' do
projects_with_access = find(".ui-box .well-list")
projects_with_access.should have_content("Mary Jane")
end
Then 'I should not see user "Mary Jane" in team list' do
projects_with_access = find(".ui-box .well-list")
projects_with_access.should_not have_content("Mary Jane")
end
Given 'project from group "Owned" has issues assigned to me' do
create :issue, create :issue,
project: project, project: project,
assignee: current_user, assignee: current_user,
author: current_user author: current_user
end end
Given 'project from group has merge requests assigned to me' do Given 'project from group "Owned" has merge requests assigned to me' do
create :merge_request, create :merge_request,
source_project: project, source_project: project,
target_project: project, target_project: project,
...@@ -71,13 +76,17 @@ class Groups < Spinach::FeatureSteps ...@@ -71,13 +76,17 @@ class Groups < Spinach::FeatureSteps
click_link "New group" click_link "New group"
end end
And 'submit form with new group info' do And 'submit form with new group "Samurai" info' do
fill_in 'group_name', with: 'Samurai' fill_in 'group_name', 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
Then 'I should see newly created group' do Then 'I should be redirected to group "Samurai" page' do
current_path.should == group_path(Group.last)
end
Then '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 "You will only see events from projects in this group" page.should have_content "You will only see events from projects in this group"
...@@ -94,57 +103,78 @@ class Groups < Spinach::FeatureSteps ...@@ -94,57 +103,78 @@ class Groups < Spinach::FeatureSteps
end end
end end
Then 'I should see new group name' do And 'I change group "Owned" name to "new-name"' do
fill_in 'group_name', with: 'new-name'
click_button "Save group"
end
Then '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 "group: new-name"
end end
end end
step 'I change my group avatar' do step 'I change group "Owned" avatar' do
attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png'))
click_button "Save group" click_button "Save group"
@group.reload Group.find_by(name: "Owned").reload
end end
step 'I should see new group avatar' do step 'I should see new group "Owned" avatar' do
@group.avatar.should be_instance_of AttachmentUploader Group.find_by(name: "Owned").avatar.should be_instance_of AttachmentUploader
@group.avatar.url.should == "/uploads/group/avatar/#{ @group.id }/gitlab_logo.png" Group.find_by(name: "Owned").avatar.url.should == "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/gitlab_logo.png"
end end
step 'I should see the "Remove avatar" button' do step 'I should see the "Remove avatar" button' do
page.should have_link("Remove avatar") page.should have_link("Remove avatar")
end end
step 'I have an group avatar' do step 'I have group "Owned" avatar' do
attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) attach_file(:group_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png'))
click_button "Save group" click_button "Save group"
@group.reload Group.find_by(name: "Owned").reload
end end
step 'I remove my group avatar' do step 'I remove group "Owned" avatar' do
click_link "Remove avatar" click_link "Remove avatar"
@group.reload Group.find_by(name: "Owned").reload
end end
step 'I should not see my group avatar' do step 'I should not see group "Owned" avatar' do
@group.avatar?.should be_false Group.find_by(name: "Owned").avatar?.should be_false
end end
step 'I should not see the "Remove avatar" button' do step 'I should not see the "Remove avatar" button' do
page.should_not have_link("Remove avatar") page.should_not have_link("Remove avatar")
end end
protected step 'I click on the "Remove User From Group" button for "John Doe"' do
find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click
# poltergeist always confirms popups.
end
def current_group step 'I click on the "Remove User From Group" button for "Mary Jane"' do
@group ||= Group.first find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click
# poltergeist always confirms popups.
end end
def project step 'I should not see the "Remove User From Group" button for "John Doe"' do
current_group.projects.first find(:css, 'li', text: "John Doe").should_not have_selector(:css, 'a.btn-remove')
# poltergeist always confirms popups.
end end
step 'I should not see the "Remove User From Group" button for "Mary Jane"' do
find(:css, 'li', text: "Mary Jane").should_not have_selector(:css, 'a.btn-remove')
# poltergeist always confirms popups.
end
protected
def assigned_to_me key def assigned_to_me key
project.send(key).where(assignee_id: current_user.id) project.send(key).where(assignee_id: current_user.id)
end end
def project
Group.find_by(name: "Owned").projects.first
end
end end
class Spinach::Features::Help < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedMarkdown
step 'I visit the help page' do
visit help_path
end
step 'I visit the "Rake Tasks" help page' do
visit help_raketasks_path
end
step 'I should see "Rake Tasks" page markdown rendered' do
page.should have_content "GitLab provides some specific rake tasks to enable special features or perform maintenance tasks"
end
step 'Header "Rebuild project satellites" should have correct ids and links' do
header_should_have_correct_id_and_link(3, 'Rebuild project satellites', 'rebuild-project-satellites')
end
end
class ProfileGroup < Spinach::FeatureSteps
include SharedAuthentication
include SharedGroup
include SharedPaths
include SharedUser
# Leave
step 'I click on the "Leave" button for group "Owned"' do
find(:css, 'li', text: "Owner").find(:css, 'i.icon-signout').click
# poltergeist always confirms popups.
end
step 'I click on the "Leave" button for group "Guest"' do
find(:css, 'li', text: "Guest").find(:css, 'i.icon-signout').click
# poltergeist always confirms popups.
end
step 'I should not see the "Leave" button for group "Owned"' do
find(:css, 'li', text: "Owner").should_not have_selector(:css, 'i.icon-signout')
# poltergeist always confirms popups.
end
step 'I should not see the "Leave" button for groupr "Guest"' do
find(:css, 'li', text: "Guest").should_not have_selector(:css, 'i.icon-signout')
# poltergeist always confirms popups.
end
step 'I should see group "Owned" in group list' do
page.should have_content("Owned")
end
step 'I should not see group "Owned" in group list' do
page.should_not have_content("Owned")
end
step 'I should see group "Guest" in group list' do
page.should have_content("Guest")
end
step 'I should not see group "Guest" in group list' do
page.should_not have_content("Guest")
end
end
class ProfileEmails < Spinach::FeatureSteps
include SharedAuthentication
Then 'I visit profile emails page' do
visit profile_emails_path
end
Then 'I should see my emails' do
page.should have_content(@user.email)
@user.emails.each do |email|
page.should have_content(email.email)
end
end
And 'I submit new email "my@email.com"' do
fill_in "email_email", with: "my@email.com"
click_button "Add"
end
Then 'I should see new email "my@email.com"' do
email = @user.emails.find_by(email: "my@email.com")
email.should_not be_nil
page.should have_content("my@email.com")
end
Then 'I should not see email "my@email.com"' do
email = @user.emails.find_by(email: "my@email.com")
email.should be_nil
page.should_not have_content("my@email.com")
end
Then 'I click link "Remove" for "my@email.com"' do
# there should only be one remove button at this time
click_link "Remove"
# force these to reload as they have been cached
@user.emails.reload
end
And 'I submit duplicate email @user.email' do
fill_in "email_email", with: @user.email
click_button "Add"
end
Then 'I should not have @user.email added' do
email = @user.emails.find_by(email: @user.email)
email.should be_nil
end
end
...@@ -8,6 +8,5 @@ class ProfileNotifications < Spinach::FeatureSteps ...@@ -8,6 +8,5 @@ class ProfileNotifications < Spinach::FeatureSteps
step 'I should see global notifications settings' do step 'I should see global notifications settings' do
page.should have_content "Notifications settings" page.should have_content "Notifications settings"
page.should have_content "Global setting"
end end
end end
class ProjectBrowseCommitsUserLookup < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Given 'I have the user that authored the commits' do
@user = create(:user, email: 'dmitriy.zaporozhets@gmail.com')
create(:email, { user: @user, email: 'dzaporozhets@sphereconsultinginc.com' })
end
Given 'I click on commit link' do
visit project_commit_path(@project, ValidCommit::ID)
end
Given 'I click on another commit link' do
visit project_commit_path(@project, ValidCommitWithAltEmail::ID)
end
Then 'I see commit info' do
page.should have_content ValidCommit::MESSAGE
check_author_link(ValidCommit::AUTHOR_EMAIL)
end
Then 'I see other commit info' do
page.should have_content ValidCommitWithAltEmail::MESSAGE
check_author_link(ValidCommitWithAltEmail::AUTHOR_EMAIL)
end
def check_author_link(email)
author_link = find('.commit-author-link')
author_link['href'].should == user_path(@user)
author_link['data-original-title'].should == email
find('.commit-author-name').text.should == @user.name
end
end
class ProjectFilterLabels < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should see "bug" in labels filter' do
within ".labels-filter" do
page.should have_content "bug"
end
end
And 'I should see "feature" in labels filter' do
within ".labels-filter" do
page.should have_content "feature"
end
end
And 'I should see "enhancement" in labels filter' do
within ".labels-filter" do
page.should have_content "enhancement"
end
end
Then 'I should see "Bugfix1" in issues list' do
within ".issues-list" do
page.should have_content "Bugfix1"
end
end
And 'I should see "Bugfix2" in issues list' do
within ".issues-list" do
page.should have_content "Bugfix2"
end
end
And 'I should not see "Bugfix2" in issues list' do
within ".issues-list" do
page.should_not have_content "Bugfix2"
end
end
And 'I should not see "Feature1" in issues list' do
within ".issues-list" do
page.should_not have_content "Feature1"
end
end
Given 'I click link "bug"' do
click_link "bug"
end
Given 'I click link "feature"' do
click_link "feature"
end
And 'project "Shop" has issue "Bugfix1" with tags: "bug", "feature"' do
project = Project.find_by(name: "Shop")
create(:issue, title: "Bugfix1", project: project, label_list: ['bug', 'feature'])
end
And 'project "Shop" has issue "Bugfix2" with tags: "bug", "enhancement"' do
project = Project.find_by(name: "Shop")
create(:issue, title: "Bugfix2", project: project, label_list: ['bug', 'enhancement'])
end
And 'project "Shop" has issue "Feature1" with tags: "feature"' do
project = Project.find_by(name: "Shop")
create(:issue, title: "Feature1", project: project, label_list: 'feature')
end
end
...@@ -3,6 +3,7 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -3,6 +3,7 @@ class ProjectIssues < Spinach::FeatureSteps
include SharedProject include SharedProject
include SharedNote include SharedNote
include SharedPaths include SharedPaths
include SharedMarkdown
Given 'I should see "Release 0.4" in issues' do Given 'I should see "Release 0.4" in issues' do
page.should have_content "Release 0.4" page.should have_content "Release 0.4"
...@@ -121,7 +122,9 @@ class ProjectIssues < Spinach::FeatureSteps ...@@ -121,7 +122,9 @@ class ProjectIssues < Spinach::FeatureSteps
create(:issue, create(:issue,
title: "Release 0.4", title: "Release 0.4",
project: project, project: project,
author: project.users.first) author: project.users.first,
description: "# Description header"
)
end end
And 'project "Shop" have "Tweet control" open issue' do And 'project "Shop" have "Tweet control" open issue' do
......
class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include SharedMarkdown
And 'I own project "Delta"' do And 'I own project "Delta"' do
@project = Project.find_by(name: "Delta") @project = Project.find_by(name: "Delta")
...@@ -44,7 +45,6 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps ...@@ -44,7 +45,6 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
page.should have_content "maintenance.md" page.should have_content "maintenance.md"
end end
And 'I click on GitLab API doc directory in README' do And 'I click on GitLab API doc directory in README' do
click_link "GitLab API doc directory" click_link "GitLab API doc directory"
end end
...@@ -140,6 +140,16 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps ...@@ -140,6 +140,16 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
page.should have_content "test GitLab API doc Rake tasks" page.should have_content "test GitLab API doc Rake tasks"
end end
step 'I add a header to the wiki page' do
fill_in "wiki[content]", with: "# Wiki header\n"
fill_in "wiki[message]", with: "Add header to wiki"
click_button "Create page"
end
step 'Wiki header should have correct id and link' do
header_should_have_correct_id_and_link(1, 'Wiki header', 'wiki-header')
end
And 'I click on test link' do And 'I click on test link' do
click_link "test" click_link "test"
end end
...@@ -173,18 +183,6 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps ...@@ -173,18 +183,6 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
page.should have_content "maintenance.md" page.should have_content "maintenance.md"
end end
Given 'I visit to the help page' do
visit help_path
end
And 'I select a page with markdown' do
click_link "Rake Tasks"
end
Then 'I should see a help page with markdown' do
page.should have_content "GitLab provides some specific rake tasks to enable special features or perform maintenance tasks"
end
Given 'I go directory which contains README file' do Given 'I go directory which contains README file' do
visit project_tree_path(@project, "master/doc/api") visit project_tree_path(@project, "master/doc/api")
current_path.should == project_tree_path(@project, "master/doc/api") current_path.should == project_tree_path(@project, "master/doc/api")
...@@ -198,4 +196,12 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps ...@@ -198,4 +196,12 @@ class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps
current_path.should == project_blob_path(@project, "master/doc/api/users.md") current_path.should == project_blob_path(@project, "master/doc/api/users.md")
page.should have_content "List users" page.should have_content "List users"
end end
step 'Header "Application details" should have correct id and link' do
header_should_have_correct_id_and_link(2, 'Application details', 'application-details')
end
step 'Header "GitLab API" should have correct id and link' do
header_should_have_correct_id_and_link(1, 'GitLab API', 'gitlab-api')
end
end end
...@@ -3,6 +3,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -3,6 +3,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
include SharedProject include SharedProject
include SharedNote include SharedNote
include SharedPaths include SharedPaths
include SharedMarkdown
step 'I click link "New Merge Request"' do step 'I click link "New Merge Request"' do
click_link "New Merge Request" click_link "New Merge Request"
...@@ -54,25 +55,15 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -54,25 +55,15 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click link "Close"' do step 'I click link "Close"' do
within '.page-title' do
click_link "Close" click_link "Close"
end end
end
step 'I submit new merge request "Wiki Feature"' do step 'I submit new merge request "Wiki Feature"' do
fill_in "merge_request_title", with: "Wiki Feature" fill_in "merge_request_title", with: "Wiki Feature"
# this must come first, so that the target branch is set
# by the time the "select" for "notes_refactoring" is executed
select project.path_with_namespace, from: "merge_request_target_project_id"
select "master", from: "merge_request_source_branch" select "master", from: "merge_request_source_branch"
find(:select, "merge_request_target_project_id", {}).value.should == project.id.to_s
find(:select, "merge_request_source_project_id", {}).value.should == project.id.to_s
# using "notes_refactoring" because "Bug NS-04" uses master/stable,
# this will fail merge_request validation if the branches are the same
find(:select, "merge_request_target_branch", {}).find(:option, "notes_refactoring", {}).value.should == "notes_refactoring"
select "notes_refactoring", from: "merge_request_target_branch" select "notes_refactoring", from: "merge_request_target_branch"
click_button "Submit merge request" click_button "Submit merge request"
end end
...@@ -83,7 +74,9 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -83,7 +74,9 @@ class ProjectMergeRequests < Spinach::FeatureSteps
target_project: project, target_project: project,
source_branch: 'stable', source_branch: 'stable',
target_branch: 'master', target_branch: 'master',
author: project.users.first) author: project.users.first,
description: "# Description header"
)
end end
step 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do step 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
...@@ -172,7 +165,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps ...@@ -172,7 +165,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I should see merged request' do step 'I should see merged request' do
within '.page-title' do within '.issue-box' do
page.should have_content "Merged" page.should have_content "Merged"
end end
end end
......
...@@ -2,6 +2,7 @@ class ProjectMilestones < Spinach::FeatureSteps ...@@ -2,6 +2,7 @@ class ProjectMilestones < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedProject include SharedProject
include SharedPaths include SharedPaths
include SharedMarkdown
Then 'I should see milestone "v2.2"' do Then 'I should see milestone "v2.2"' do
milestone = @project.milestones.find_by(title: "v2.2") milestone = @project.milestones.find_by(title: "v2.2")
...@@ -32,8 +33,11 @@ class ProjectMilestones < Spinach::FeatureSteps ...@@ -32,8 +33,11 @@ class ProjectMilestones < Spinach::FeatureSteps
And 'project "Shop" has milestone "v2.2"' do And 'project "Shop" has milestone "v2.2"' do
project = Project.find_by(name: "Shop") project = Project.find_by(name: "Shop")
milestone = create(:milestone, title: "v2.2", project: project) milestone = create(:milestone,
title: "v2.2",
project: project,
description: "# Description header"
)
3.times { create(:issue, project: project, milestone: milestone) } 3.times { create(:issue, project: project, milestone: milestone) }
end end
......
class ProjectNetworkGraph < Spinach::FeatureSteps class ProjectNetworkGraph < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths
include SharedProject include SharedProject
Then 'page should have network graph' do Then 'page should have network graph' do
......
...@@ -3,12 +3,8 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -3,12 +3,8 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
include SharedPaths include SharedPaths
include SharedProject include SharedProject
step 'I should see project "Community"' do step 'public empty project "Empty Public Project"' do
page.should have_content "Community" create :empty_project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end
step 'I should not see project "Enterprise"' do
page.should_not have_content "Enterprise"
end end
step 'I should see project "Empty Public Project"' do step 'I should see project "Empty Public Project"' do
...@@ -24,14 +20,6 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -24,14 +20,6 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
page.should have_content 'README.md' page.should have_content 'README.md'
end end
step 'public project "Community"' do
create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end
step 'public empty project "Empty Public Project"' do
create :empty_project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end
step 'I visit empty project page' do step 'I visit empty project page' do
project = Project.find_by(name: 'Empty Public Project') project = Project.find_by(name: 'Empty Public Project')
visit project_path(project) visit project_path(project)
...@@ -60,10 +48,6 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -60,10 +48,6 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end end
end end
step 'private project "Enterprise"' do
create :project, name: 'Enterprise'
end
step 'I visit project "Enterprise" page' do step 'I visit project "Enterprise" page' do
project = Project.find_by(name: 'Enterprise') project = Project.find_by(name: 'Enterprise')
visit project_path(project) visit project_path(project)
...@@ -75,18 +59,6 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps ...@@ -75,18 +59,6 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps
end end
end end
step 'internal project "Internal"' do
create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
end
step 'I should see project "Internal"' do
page.should have_content "Internal"
end
step 'I should not see project "Internal"' do
page.should_not have_content "Internal"
end
step 'I visit project "Internal" page' do step 'I visit project "Internal" page' do
project = Project.find_by(name: 'Internal') project = Project.find_by(name: 'Internal')
visit project_path(project) visit project_path(project)
......
...@@ -12,6 +12,14 @@ module SharedAuthentication ...@@ -12,6 +12,14 @@ module SharedAuthentication
login_as :admin login_as :admin
end end
step 'I sign in as "John Doe"' do
login_with(user_exists("John Doe"))
end
step 'I sign in as "Mary Jane"' do
login_with(user_exists("Mary Jane"))
end
step 'I should be redirected to sign in page' do step 'I should be redirected to sign in page' do
current_path.should == new_user_session_path current_path.should == new_user_session_path
end end
......
module SharedGroup
include Spinach::DSL
step '"John Doe" is owner of group "Owned"' do
is_member_of("John Doe", "Owned", Gitlab::Access::OWNER)
end
step '"John Doe" is guest of group "Guest"' do
is_member_of("John Doe", "Guest", Gitlab::Access::GUEST)
end
step '"Mary Jane" is owner of group "Owned"' do
is_member_of("Mary Jane", "Owned", Gitlab::Access::OWNER)
end
step '"Mary Jane" is guest of group "Owned"' do
is_member_of("Mary Jane", "Owned", Gitlab::Access::GUEST)
end
step '"Mary Jane" is guest of group "Guest"' do
is_member_of("Mary Jane", "Guest", Gitlab::Access::GUEST)
end
protected
def is_member_of(username, groupname, role)
@project_count ||= 0
user = User.find_by(name: username) || create(:user, name: username)
group = Group.find_by(name: groupname) || create(:group, name: groupname)
group.add_user(user, role)
project ||= create(:project, namespace: group, path: "project#{@project_count}")
event ||= create(:closed_issue_event, project: project)
project.team << [user, :master]
@project_count += 1
end
end
module SharedMarkdown
include Spinach::DSL
def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
page.find(:css, "#{parent} h#{level}##{id}").text.should == text
page.find(:css, "#{parent} h#{level}##{id} > :last-child")[:href].should =~ /##{id}$/
end
step 'Header "Description header" should have correct id and link' do
header_should_have_correct_id_and_link(1, 'Description header', 'description-header')
end
end
...@@ -102,4 +102,21 @@ module SharedNote ...@@ -102,4 +102,21 @@ module SharedNote
page.should have_content("XML attached") page.should have_content("XML attached")
end end
end end
# Markdown
step 'I leave a comment with a header containing "Comment with a header"' do
within(".js-main-target-form") do
fill_in "note[note]", with: "# Comment with a header"
click_button "Add Comment"
sleep 0.05
end
end
step 'The comment with the header should not have an ID' do
within(".note-text") do
page.should have_content("Comment with a header")
page.should_not have_css("#comment-with-a-header")
end
end
end end
...@@ -5,28 +5,56 @@ module SharedPaths ...@@ -5,28 +5,56 @@ module SharedPaths
visit new_project_path visit new_project_path
end end
# ----------------------------------------
# User
# ----------------------------------------
step 'I visit user "John Doe" page' do
visit user_path("john_doe")
end
# ---------------------------------------- # ----------------------------------------
# Group # Group
# ---------------------------------------- # ----------------------------------------
step 'I visit group page' do step 'I visit group "Owned" page' do
visit group_path(current_group) visit group_path(Group.find_by(name:"Owned"))
end
step 'I visit group "Owned" issues page' do
visit issues_group_path(Group.find_by(name:"Owned"))
end
step 'I visit group "Owned" merge requests page' do
visit merge_requests_group_path(Group.find_by(name:"Owned"))
end
step 'I visit group "Owned" members page' do
visit members_group_path(Group.find_by(name:"Owned"))
end
step 'I visit group "Owned" settings page' do
visit edit_group_path(Group.find_by(name:"Owned"))
end
step 'I visit group "Guest" page' do
visit group_path(Group.find_by(name:"Guest"))
end end
step 'I visit group issues page' do step 'I visit group "Guest" issues page' do
visit issues_group_path(current_group) visit issues_group_path(Group.find_by(name:"Guest"))
end end
step 'I visit group merge requests page' do step 'I visit group "Guest" merge requests page' do
visit merge_requests_group_path(current_group) visit merge_requests_group_path(Group.find_by(name:"Guest"))
end end
step 'I visit group members page' do step 'I visit group "Guest" members page' do
visit members_group_path(current_group) visit members_group_path(Group.find_by(name:"Guest"))
end end
step 'I visit group settings page' do step 'I visit group "Guest" settings page' do
visit edit_group_path(current_group) visit edit_group_path(Group.find_by(name:"Guest"))
end end
# ---------------------------------------- # ----------------------------------------
...@@ -85,6 +113,14 @@ module SharedPaths ...@@ -85,6 +113,14 @@ module SharedPaths
visit history_profile_path visit history_profile_path
end end
step 'I visit profile groups page' do
visit profile_groups_path
end
step 'I should be redirected to the profile groups page' do
current_path.should == profile_groups_path
end
# ---------------------------------------- # ----------------------------------------
# Admin # Admin
# ---------------------------------------- # ----------------------------------------
...@@ -318,4 +354,12 @@ module SharedPaths ...@@ -318,4 +354,12 @@ module SharedPaths
def project def project
project = Project.find_by!(name: "Shop") project = Project.find_by!(name: "Shop")
end end
# ----------------------------------------
# Errors
# ----------------------------------------
Then 'page status code should be 404' do
page.status_code.should == 404
end
end end
...@@ -58,11 +58,68 @@ module SharedProject ...@@ -58,11 +58,68 @@ module SharedProject
page.should have_content("Features:") page.should have_content("Features:")
end end
Then 'page status code should be 404' do
page.status_code.should == 404
end
def current_project def current_project
@project ||= Project.first @project ||= Project.first
end end
# ----------------------------------------
# Visibility level
# ----------------------------------------
step 'private project "Enterprise"' do
create :project, name: 'Enterprise'
end
step 'I should see project "Enterprise"' do
page.should have_content "Enterprise"
end
step 'I should not see project "Enterprise"' do
page.should_not have_content "Enterprise"
end
step 'internal project "Internal"' do
create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
end
step 'I should see project "Internal"' do
page.should have_content "Internal"
end
step 'I should not see project "Internal"' do
page.should_not have_content "Internal"
end
step 'public project "Community"' do
create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
end
step 'I should see project "Community"' do
page.should have_content "Community"
end
step 'I should not see project "Community"' do
page.should_not have_content "Community"
end
step '"John Doe" is authorized to private project "Enterprise"' do
user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Enterprise")
project ||= create(:project, name: "Enterprise", namespace: user.namespace)
project.team << [user, :master]
end
step '"John Doe" is authorized to internal project "Internal"' do
user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Internal")
project ||= create :project, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL
project.team << [user, :master]
end
step '"John Doe" is authorized to public project "Community"' do
user = user_exists("John Doe", username: "john_doe")
project = Project.find_by(name: "Community")
project ||= create :project, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC
project.team << [user, :master]
end
end end
module SharedUser
include Spinach::DSL
step 'User "John Doe" exists' do
user_exists("John Doe", {username: "john_doe"})
end
step 'User "Mary Jane" exists' do
user_exists("Mary Jane", {username: "mary_jane"})
end
protected
def user_exists(name, options = {})
User.find_by(name: name) || create(:user, {name: name, admin: false}.merge(options))
end
end
class Spinach::Features::User < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedUser
include SharedProject
step 'I should see user "John Doe" page' do
expect(page.title).to match(/^\s*John Doe/)
end
end
...@@ -15,7 +15,7 @@ require 'spinach/capybara' ...@@ -15,7 +15,7 @@ require 'spinach/capybara'
require 'sidekiq/testing/inline' require 'sidekiq/testing/inline'
%w(valid_commit big_commits select2_helper test_env).each do |f| %w(valid_commit valid_commit_with_alt_email big_commits select2_helper test_env).each do |f|
require Rails.root.join('spec', 'support', f) require Rails.root.join('spec', 'support', f)
end end
......
Feature: User
Background:
Given User "John Doe" exists
And "John Doe" is authorized to private project "Enterprise"
# Signed out
Scenario: I visit user "John Doe" page while not signed in when he is authorized to a public project
Given "John Doe" is authorized to internal project "Internal"
And "John Doe" is authorized to public project "Community"
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should not see project "Internal"
And I should see project "Community"
Scenario: I visit user "John Doe" page while not signed in when he is not authorized to a public project
Given "John Doe" is authorized to internal project "Internal"
When I visit user "John Doe" page
Then I should be redirected to sign in page
# Signed in as someone else
Scenario: I visit user "John Doe" page while signed in as someone else when he is authorized to a public project
Given "John Doe" is authorized to public project "Community"
And "John Doe" is authorized to internal project "Internal"
And I sign in as a user
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should see project "Internal"
And I should see project "Community"
Scenario: I visit user "John Doe" page while signed in as someone else when he is not authorized to a public project
Given "John Doe" is authorized to internal project "Internal"
And I sign in as a user
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should see project "Internal"
And I should not see project "Community"
Scenario: I visit user "John Doe" page while signed in as someone else when he is not authorized to a project I can see
Given I sign in as a user
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should not see project "Enterprise"
And I should not see project "Internal"
And I should not see project "Community"
# Signed in as the user himself
Scenario: I visit user "John Doe" page while signed in as "John Doe" when he has a public project
Given "John Doe" is authorized to internal project "Internal"
And "John Doe" is authorized to public project "Community"
And I sign in as "John Doe"
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should see project "Enterprise"
And I should see project "Internal"
And I should see project "Community"
Scenario: I visit user "John Doe" page while signed in as "John Doe" when he has no public project
Given I sign in as "John Doe"
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should see project "Enterprise"
And I should not see project "Internal"
And I should not see project "Community"
...@@ -22,6 +22,8 @@ module API ...@@ -22,6 +22,8 @@ module API
end end
format :json format :json
content_type :txt, "text/plain"
helpers APIHelpers helpers APIHelpers
mount Groups mount Groups
...@@ -41,6 +43,7 @@ module API ...@@ -41,6 +43,7 @@ module API
mount Ldap mount Ldap
mount Services mount Services
mount Files mount Files
mount Commits
mount Namespaces mount Namespaces
end end
end end
require 'mime/types'
module API
# Projects API
class Commits < Grape::API
before { authenticate! }
before { authorize! :download_code, user_project }
resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
# Get a project repository commits
#
# Parameters:
# id (required) - The ID of a project
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# Example Request:
# GET /projects/:id/repository/commits
get ":id/repository/commits" do
page = (params[:page] || 0).to_i
per_page = (params[:per_page] || 20).to_i
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
present commits, with: Entities::RepoCommit
end
# Get a specific commit of a project
#
# Parameters:
# id (required) - The ID of a project
# sha (required) - The commit hash or name of a repository branch or tag
# Example Request:
# GET /projects/:id/repository/commits/:sha
get ":id/repository/commits/:sha" do
sha = params[:sha]
commit = user_project.repository.commit(sha)
not_found! "Commit" unless commit
present commit, with: Entities::RepoCommitDetail
end
# Get the diff for a specific commit of a project
#
# Parameters:
# id (required) - The ID of a project
# sha (required) - The commit or branch name
# Example Request:
# GET /projects/:id/repository/commits/:sha/diff
get ":id/repository/commits/:sha/diff" do
sha = params[:sha]
commit = user_project.repository.commit(sha)
not_found! "Commit" unless commit
commit.diffs
end
end
end
end
...@@ -6,6 +6,12 @@ module API ...@@ -6,6 +6,12 @@ module API
expose :is_admin?, as: :is_admin expose :is_admin?, as: :is_admin
expose :can_create_group?, as: :can_create_group expose :can_create_group?, as: :can_create_group
expose :can_create_project?, as: :can_create_project expose :can_create_project?, as: :can_create_project
expose :avatar_url do |user, options|
if user.avatar.present?
user.avatar.url
end
end
end end
class UserSafe < Grape::Entity class UserSafe < Grape::Entity
......
...@@ -5,10 +5,61 @@ module API ...@@ -5,10 +5,61 @@ module API
before { authorize! :push_code, user_project } before { authorize! :push_code, user_project }
resource :projects do resource :projects do
# Get file from repository
# File content is Base64 encoded
#
# Parameters:
# file_path (required) - The path to the file. Ex. lib/class.rb
# ref (required) - The name of branch, tag or commit
#
# Example Request:
# GET /projects/:id/repository/files
#
# Example response:
# {
# "file_name": "key.rb",
# "file_path": "app/models/key.rb",
# "size": 1476,
# "encoding": "base64",
# "content": "IyA9PSBTY2hlbWEgSW5mb3...",
# "ref": "master",
# "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
# "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50"
# }
#
get ":id/repository/files" do
required_attributes! [:file_path, :ref]
attrs = attributes_for_keys [:file_path, :ref]
ref = attrs.delete(:ref)
file_path = attrs.delete(:file_path)
commit = user_project.repository.commit(ref)
not_found! "Commit" unless commit
blob = user_project.repository.blob_at(commit.sha, file_path)
if blob
status(200)
{
file_name: blob.name,
file_path: blob.path,
size: blob.size,
encoding: "base64",
content: Base64.encode64(blob.data),
ref: ref,
blob_id: blob.id,
commit_id: commit.id,
}
else
render_api_error!('File not found', 404)
end
end
# Create new file in repository # Create new file in repository
# #
# Parameters: # Parameters:
# file_path (optional) - The path to new file. Ex. lib/class.rb # file_path (required) - The path to new file. Ex. lib/class.rb
# branch_name (required) - The name of branch # branch_name (required) - The name of branch
# content (required) - File content # content (required) - File content
# commit_message (required) - Commit message # commit_message (required) - Commit message
......
...@@ -36,11 +36,15 @@ module API ...@@ -36,11 +36,15 @@ module API
return false if user.blocked? return false if user.blocked?
if Gitlab.config.ldap.enabled
if user.ldap_user? if user.ldap_user?
# Check if LDAP user exists and match LDAP user_filter # Check if LDAP user exists and match LDAP user_filter
unless Gitlab::LDAP::Access.new.allowed?(user) unless Gitlab::LDAP::Access.new.allowed?(user)
return false return false
end end
return false if user.ldap_user? && Gitlab::LDAP::User.blocked?(user.extern_uid)
end
end end
action = case git_cmd action = case git_cmd
......
...@@ -116,8 +116,6 @@ module API ...@@ -116,8 +116,6 @@ module API
authorize! :modify_merge_request, merge_request authorize! :modify_merge_request, merge_request
if merge_request.update_attributes attrs if merge_request.update_attributes attrs
merge_request.reload_code
merge_request.mark_as_unchecked
present merge_request, with: Entities::MergeRequest present merge_request, with: Entities::MergeRequest
else else
handle_merge_request_errors! merge_request.errors handle_merge_request_errors! merge_request.errors
......
...@@ -331,6 +331,18 @@ module API ...@@ -331,6 +331,18 @@ module API
projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%") projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%")
present paginate(projects), with: Entities::Project present paginate(projects), with: Entities::Project
end end
# Get a users list
#
# Example Request:
# GET /users
get ':id/users' do
@users = User.where(id: user_project.team.users.map(&:id))
@users = @users.search(params[:search]) if params[:search].present?
@users = paginate @users
present @users, with: Entities::User
end
end end
end end
end end
...@@ -85,50 +85,6 @@ module API ...@@ -85,50 +85,6 @@ module API
present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject, project: user_project present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject, project: user_project
end end
# Get a project repository commits
#
# Parameters:
# id (required) - The ID of a project
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# Example Request:
# GET /projects/:id/repository/commits
get ":id/repository/commits" do
page = (params[:page] || 0).to_i
per_page = (params[:per_page] || 20).to_i
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
present commits, with: Entities::RepoCommit
end
# Get a specific commit of a project
#
# Parameters:
# id (required) - The ID of a project
# sha (required) - The commit hash or name of a repository branch or tag
# Example Request:
# GET /projects/:id/repository/commits/:sha
get ":id/repository/commits/:sha" do
sha = params[:sha]
commit = user_project.repository.commit(sha)
not_found! "Commit" unless commit
present commit, with: Entities::RepoCommitDetail
end
# Get the diff for a specific commit of a project
#
# Parameters:
# id (required) - The ID of a project
# sha (required) - The commit or branch name
# Example Request:
# GET /projects/:id/repository/commits/:sha/diff
get ":id/repository/commits/:sha/diff" do
sha = params[:sha]
commit = user_project.repository.commit(sha)
not_found! "Commit" unless commit
commit.diffs
end
# Get a project repository tree # Get a project repository tree
# #
# Parameters: # Parameters:
...@@ -167,9 +123,7 @@ module API ...@@ -167,9 +123,7 @@ module API
blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath]) blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath])
not_found! "File" unless blob not_found! "File" unless blob
env['api.format'] = :txt content_type 'text/plain'
content_type blob.mime_type
present blob.data present blob.data
end end
......
...@@ -8,6 +8,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -8,6 +8,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
@project = @template.instance_variable_get("@project") @project = @template.instance_variable_get("@project")
@ref = @template.instance_variable_get("@ref") @ref = @template.instance_variable_get("@ref")
@request_path = @template.instance_variable_get("@path") @request_path = @template.instance_variable_get("@path")
@options = options.dup
super options super options
end end
...@@ -34,6 +35,16 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -34,6 +35,16 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
h.link_to_gfm(content, link, title: title) h.link_to_gfm(content, link, title: title)
end end
def header(text, level)
if @options[:no_header_anchors]
"<h#{level}>#{text}</h#{level}>"
else
id = ActionController::Base.helpers.strip_tags(h.gfm(text)).downcase() \
.gsub(/[^a-z0-9_-]/, '-').gsub(/-+/, '-').gsub(/^-/, '').gsub(/-$/, '')
"<h#{level} id=\"#{id}\">#{text}<a href=\"\##{id}\"></a></h#{level}>"
end
end
def preprocess(full_document) def preprocess(full_document)
if @project if @project
h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?) h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?)
......
...@@ -20,5 +20,29 @@ namespace :gitlab do ...@@ -20,5 +20,29 @@ namespace :gitlab do
puts "Importing #{user.email} users into #{project_ids.size} projects" puts "Importing #{user.email} users into #{project_ids.size} projects"
UsersProject.add_users_into_projects(project_ids, Array.wrap(user.id), UsersProject::DEVELOPER) UsersProject.add_users_into_projects(project_ids, Array.wrap(user.id), UsersProject::DEVELOPER)
end end
desc "GITLAB | Add all users to all groups (admin users are added as owners)"
task all_users_to_all_groups: :environment do |t, args|
user_ids = User.where(admin: false).pluck(:id)
admin_ids = User.where(admin: true).pluck(:id)
groups = Group.all
puts "Importing #{user_ids.size} users into #{groups.size} groups"
puts "Importing #{admin_ids.size} admins into #{groups.size} groups"
groups.each do |group|
group.add_users(user_ids, UsersGroup::DEVELOPER)
group.add_users(admin_ids, UsersGroup::OWNER)
end
end
desc "GITLAB | Add a specific user to all groups (as a developer)"
task :user_to_groups, [:email] => :environment do |t, args|
user = User.find_by_email args.email
groups = Group.all
puts "Importing #{user.email} users into #{groups.size} groups"
groups.each do |group|
group.add_users(Array.wrap(user.id), UsersGroup::DEVELOPER)
end
end
end end
end end
#!/bin/bash #!/usr/bin/env bash
cd $(dirname $0)/.. cd $(dirname $0)/..
app_root=$(pwd) app_root=$(pwd)
......
#!/bin/bash #!/usr/bin/env bash
cd $(dirname $0)/.. cd $(dirname $0)/..
app_root=$(pwd) app_root=$(pwd)
......
require 'spec_helper'
describe Profiles::KeysController do
let(:user) { create(:user) }
describe "#get_keys" do
describe "non existant user" do
it "should generally not work" do
get :get_keys, username: 'not-existent'
expect(response).not_to be_success
end
end
describe "user with no keys" do
it "should generally work" do
get :get_keys, username: user.username
expect(response).to be_success
end
it "should render all keys separated with a new line" do
get :get_keys, username: user.username
expect(response.body).to eq("")
end
end
describe "user with keys" do
before do
user.keys << create(:key)
user.keys << create(:another_key)
end
it "should generally work" do
get :get_keys, username: user.username
expect(response).to be_success
end
it "should render all keys separated with a new line" do
get :get_keys, username: user.username
expect(response.body).not_to eq("")
expect(response.body).to eq(user.all_ssh_keys.join("\n"))
end
end
end
end
...@@ -207,6 +207,12 @@ FactoryGirl.define do ...@@ -207,6 +207,12 @@ FactoryGirl.define do
end end
end end
factory :another_key do
key do
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmTillFzNTrrGgwaCKaSj+QCz81E6jBc/s9av0+3b1Hwfxgkqjl4nAK/OD2NjgyrONDTDfR8cRN4eAAy6nY8GLkOyYBDyuc5nTMqs5z3yVuTwf3koGm/YQQCmo91psZ2BgDFTor8SVEE5Mm1D1k3JDMhDFxzzrOtRYFPci9lskTJaBjpqWZ4E9rDTD2q/QZntCqbC3wE9uSemRQB5f8kik7vD/AD8VQXuzKladrZKkzkONCPWsXDspUitjM8HkQdOf0PsYn1CMUC1xKYbCxkg5TkEosIwGv6CoEArUrdu/4+10LVslq494mAvEItywzrluCLCnwELfW+h/m8UHoVhZ"
end
end
factory :invalid_key do factory :invalid_key do
key do key do
"ssh-rsa this_is_invalid_key==" "ssh-rsa this_is_invalid_key=="
...@@ -214,6 +220,19 @@ FactoryGirl.define do ...@@ -214,6 +220,19 @@ FactoryGirl.define do
end end
end end
factory :email do
user
email do
Faker::Internet.email('alias')
end
factory :another_email do
email do
Faker::Internet.email('another.alias')
end
end
end
factory :milestone do factory :milestone do
title title
project project
......
...@@ -183,10 +183,10 @@ describe "Issues" do ...@@ -183,10 +183,10 @@ describe "Issues" do
it 'with dropdown menu' do it 'with dropdown menu' do
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
find('.edit-issue.inline-update').select(project.team.members.first.name, from: 'issue_assignee_id') find('.edit-issue.inline-update #issue_assignee_id').set project.team.members.first.id
click_button 'Update Issue' click_button 'Update Issue'
page.should have_content "currently assigned to" page.should have_content "Assignee:"
page.has_select?('issue_assignee_id', :selected => project.team.members.first.name) page.has_select?('issue_assignee_id', :selected => project.team.members.first.name)
end end
end end
...@@ -206,11 +206,9 @@ describe "Issues" do ...@@ -206,11 +206,9 @@ describe "Issues" do
login_with guest login_with guest
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
page.should have_content "currently assigned to #{issue.assignee.name}" page.should have_content issue.assignee.name
end end
end end
end end
describe 'update milestone from issue#show' do describe 'update milestone from issue#show' do
...@@ -225,17 +223,16 @@ describe "Issues" do ...@@ -225,17 +223,16 @@ describe "Issues" do
find('.edit-issue.inline-update').select(milestone.title, from: 'issue_milestone_id') find('.edit-issue.inline-update').select(milestone.title, from: 'issue_milestone_id')
click_button 'Update Issue' click_button 'Update Issue'
page.should have_content "Attached to milestone" page.should have_content "Milestone"
page.has_select?('issue_assignee_id', :selected => milestone.title) page.has_select?('issue_assignee_id', :selected => milestone.title)
end end
end end
context 'by unauthorized user' do context 'by unauthorized user' do
let(:guest) { create(:user) } let(:guest) { create(:user) }
before :each do before :each do
project.team << [[guest], :guest] project.team << [guest, :guest]
issue.milestone = milestone issue.milestone = milestone
issue.save issue.save
end end
...@@ -245,8 +242,7 @@ describe "Issues" do ...@@ -245,8 +242,7 @@ describe "Issues" do
login_with guest login_with guest
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
page.should have_content milestone.title
page.should have_content "Attached to milestone #{milestone.title}"
end end
end end
end end
......
...@@ -348,8 +348,21 @@ describe GitlabMarkdownHelper do ...@@ -348,8 +348,21 @@ describe GitlabMarkdownHelper do
it "should handle references in headers" do it "should handle references in headers" do
actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}" actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}"
markdown(actual).should match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>}) markdown(actual, {no_header_anchors:true}).should match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>})
markdown(actual).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>}) markdown(actual, {no_header_anchors:true}).should match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>})
end
it "should add ids and links to headers" do
# Test every rule except nested tags.
text = '..Ab_c-d. e..'
id = 'ab_c-d-e'
markdown("# #{text}").should match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>})
markdown("# #{text}", {no_header_anchors:true}).should == "<h1>#{text}</h1>"
id = 'link-text'
markdown("# [link text](url) ![img alt](url)").should match(
%r{<h1 id="#{id}"><a href="[^"]*url">link text</a> <img[^>]*><a href="[^"]*##{id}"></a></h1>}
)
end end
it "should handle references in lists" do it "should handle references in lists" do
......
...@@ -8,7 +8,7 @@ describe NotificationsHelper do ...@@ -8,7 +8,7 @@ describe NotificationsHelper do
before { notification.stub(disabled?: true) } before { notification.stub(disabled?: true) }
it "has a red icon" do it "has a red icon" do
notification_icon(notification).should match('class="icon-circle cred"') notification_icon(notification).should match('class="icon-volume-off cred"')
end end
end end
...@@ -16,7 +16,7 @@ describe NotificationsHelper do ...@@ -16,7 +16,7 @@ describe NotificationsHelper do
before { notification.stub(participating?: true) } before { notification.stub(participating?: true) }
it "has a blue icon" do it "has a blue icon" do
notification_icon(notification).should match('class="icon-circle cblue"') notification_icon(notification).should match('class="icon-volume-down cblue"')
end end
end end
...@@ -24,7 +24,7 @@ describe NotificationsHelper do ...@@ -24,7 +24,7 @@ describe NotificationsHelper do
before { notification.stub(watch?: true) } before { notification.stub(watch?: true) }
it "has a green icon" do it "has a green icon" do
notification_icon(notification).should match('class="icon-circle cgreen"') notification_icon(notification).should match('class="icon-volume-up cgreen"')
end end
end end
......
...@@ -90,6 +90,28 @@ describe Notify do ...@@ -90,6 +90,28 @@ describe Notify do
end end
end end
describe 'user added email' do
let(:email) { create(:email) }
subject { Notify.new_email_email(email.id) }
it 'is sent to the new user' do
should deliver_to email.user.email
end
it 'has the correct subject' do
should have_subject /^gitlab \| Email was added to your account$/i
end
it 'contains the new email address' do
should have_body_text /#{email.email}/
end
it 'includes a link to emails page' do
should have_body_text /#{profile_emails_path}/
end
end
context 'for a project' do context 'for a project' do
describe 'items that are assignable, the email' do describe 'items that are assignable, the email' do
let(:assignee) { create(:user, email: 'assignee@example.com') } let(:assignee) { create(:user, email: 'assignee@example.com') }
......
...@@ -303,6 +303,17 @@ describe User do ...@@ -303,6 +303,17 @@ describe User do
end end
end end
describe 'all_ssh_keys' do
it { should have_many(:keys).dependent(:destroy) }
it "should have all ssh keys" do
user = create :user
key = create :key, key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD33bWLBxu48Sev9Fert1yzEO4WGcWglWF7K/AwblIUFselOt/QdOL9DSjpQGxLagO1s9wl53STIO8qGS4Ms0EJZyIXOEFMjFJ5xmjSy+S37By4sG7SsltQEHMxtbtFOaW5LV2wCrX+rUsRNqLMamZjgjcPO0/EgGCXIGMAYW4O7cwGZdXWYIhQ1Vwy+CsVMDdPkPgBXqK7nR/ey8KMs8ho5fMNgB5hBw/AL9fNGhRw3QTD6Q12Nkhl4VZES2EsZqlpNnJttnPdp847DUsT6yuLRlfiQfz5Cn9ysHFdXObMN5VYIiPFwHeYCZp1X2S4fDZooRE8uOLTfxWHPXwrhqSH", user_id: user.id
user.all_ssh_keys.should include(key.key)
end
end
describe :avatar_type do describe :avatar_type do
let(:user) { create(:user) } let(:user) { create(:user) }
......
require 'spec_helper'
describe EmailObserver do
let(:email) { create(:email) }
before { subject.stub(notification: double('NotificationService').as_null_object) }
subject { EmailObserver.instance }
describe '#after_create' do
it 'trigger notification to send emails' do
subject.should_receive(:notification)
subject.after_create(email)
end
end
end
require 'spec_helper'
require 'mime/types'
describe API::API do
include ApiHelpers
before(:each) { enable_observers }
after(:each) {disable_observers}
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
let!(:master) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:guest) { create(:users_project, user: user2, project: project, project_access: UsersProject::GUEST) }
before { project.team << [user, :reporter] }
describe "GET /projects/:id/repository/commits" do
context "authorized user" do
before { project.team << [user2, :reporter] }
it "should return project commits" do
get api("/projects/#{project.id}/repository/commits", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['id'].should == project.repository.commit.id
end
end
context "unauthorized user" do
it "should not return project commits" do
get api("/projects/#{project.id}/repository/commits")
response.status.should == 401
end
end
end
describe "GET /projects:id/repository/commits/:sha" do
context "authorized user" do
it "should return a commit by sha" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
response.status.should == 200
json_response['id'].should == project.repository.commit.id
json_response['title'].should == project.repository.commit.title
end
it "should return a 404 error if not found" do
get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
response.status.should == 404
end
end
context "unauthorized user" do
it "should not return the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}")
response.status.should == 401
end
end
end
describe "GET /projects:id/repository/commits/:sha/diff" do
context "authorized user" do
before { project.team << [user2, :reporter] }
it "should return the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should >= 1
json_response.first.keys.should include "diff"
end
it "should return a 404 error if invalid commit" do
get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user)
response.status.should == 404
end
end
context "unauthorized user" do
it "should not return the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff")
response.status.should == 401
end
end
end
end
...@@ -9,6 +9,36 @@ describe API::API do ...@@ -9,6 +9,36 @@ describe API::API do
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project, namespace: user.namespace ) }
before { project.team << [user, :developer] } before { project.team << [user, :developer] }
describe "GET /projects/:id/repository/files" do
it "should return file info" do
params = {
file_path: 'app/models/key.rb',
ref: 'master',
}
get api("/projects/#{project.id}/repository/files", user), params
response.status.should == 200
json_response['file_path'].should == 'app/models/key.rb'
json_response['file_name'].should == 'key.rb'
Base64.decode64(json_response['content']).lines.first.should == "class Key < ActiveRecord::Base\n"
end
it "should return a 400 bad request if no params given" do
get api("/projects/#{project.id}/repository/files", user)
response.status.should == 400
end
it "should return a 404 if such file does not exist" do
params = {
file_path: 'app/models/application.rb',
ref: 'master',
}
get api("/projects/#{project.id}/repository/files", user), params
response.status.should == 404
end
end
describe "POST /projects/:id/repository/files" do describe "POST /projects/:id/repository/files" do
let(:valid_params) { let(:valid_params) {
{ {
......
...@@ -103,77 +103,6 @@ describe API::API do ...@@ -103,77 +103,6 @@ describe API::API do
end end
end end
describe "GET /projects/:id/repository/commits" do
context "authorized user" do
before { project.team << [user2, :reporter] }
it "should return project commits" do
get api("/projects/#{project.id}/repository/commits", user)
response.status.should == 200
json_response.should be_an Array
json_response.first['id'].should == project.repository.commit.id
end
end
context "unauthorized user" do
it "should not return project commits" do
get api("/projects/#{project.id}/repository/commits")
response.status.should == 401
end
end
end
describe "GET /projects:id/repository/commits/:sha" do
context "authorized user" do
it "should return a commit by sha" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
response.status.should == 200
json_response['id'].should == project.repository.commit.id
json_response['title'].should == project.repository.commit.title
end
it "should return a 404 error if not found" do
get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
response.status.should == 404
end
end
context "unauthorized user" do
it "should not return the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}")
response.status.should == 401
end
end
end
describe "GET /projects:id/repository/commits/:sha/diff" do
context "authorized user" do
before { project.team << [user2, :reporter] }
it "should return the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user)
response.status.should == 200
json_response.should be_an Array
json_response.length.should >= 1
json_response.first.keys.should include "diff"
end
it "should return a 404 error if invalid commit" do
get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user)
response.status.should == 404
end
end
context "unauthorized user" do
it "should not return the diff of the selected commit" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff")
response.status.should == 401
end
end
end
describe "GET /projects/:id/repository/tree" do describe "GET /projects/:id/repository/tree" do
context "authorized user" do context "authorized user" do
before { project.team << [user2, :reporter] } before { project.team << [user2, :reporter] }
......
...@@ -176,6 +176,28 @@ describe Profiles::KeysController, "routing" do ...@@ -176,6 +176,28 @@ describe Profiles::KeysController, "routing" do
it "to #destroy" do it "to #destroy" do
delete("/profile/keys/1").should route_to('profiles/keys#destroy', id: '1') delete("/profile/keys/1").should route_to('profiles/keys#destroy', id: '1')
end end
# get all the ssh-keys of a user
it "to #get_keys" do
get("/foo.keys").should route_to('profiles/keys#get_keys', username: 'foo')
end
end
# emails GET /emails(.:format) emails#index
# POST /keys(.:format) emails#create
# DELETE /keys/:id(.:format) keys#destroy
describe Profiles::EmailsController, "routing" do
it "to #index" do
get("/profile/emails").should route_to('profiles/emails#index')
end
it "to #create" do
post("/profile/emails").should route_to('profiles/emails#create')
end
it "to #destroy" do
delete("/profile/emails/1").should route_to('profiles/emails#destroy', id: '1')
end
end end
# profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy # profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy
......
...@@ -16,6 +16,19 @@ describe NotificationService do ...@@ -16,6 +16,19 @@ describe NotificationService do
end end
end end
describe 'Email' do
describe :new_email do
let(:email) { create(:email) }
it { notification.new_email(email).should be_true }
it 'should send email to email owner' do
Notify.should_receive(:new_email_email).with(email.id)
notification.new_email(email)
end
end
end
describe 'Notes' do describe 'Notes' do
context 'issue note' do context 'issue note' do
let(:issue) { create(:issue, assignee: create(:user)) } let(:issue) { create(:issue, assignee: create(:user)) }
......
...@@ -2,6 +2,7 @@ module ValidCommit ...@@ -2,6 +2,7 @@ module ValidCommit
ID = "8470d70da67355c9c009e4401746b1d5410af2e3" ID = "8470d70da67355c9c009e4401746b1d5410af2e3"
MESSAGE = "notes controller refactored" MESSAGE = "notes controller refactored"
AUTHOR_FULL_NAME = "Dmitriy Zaporozhets" AUTHOR_FULL_NAME = "Dmitriy Zaporozhets"
AUTHOR_EMAIL = "dmitriy.zaporozhets@gmail.com"
FILES = [".foreman", ".gitignore", ".rails_footnotes", ".rspec", ".travis.yml", "CHANGELOG", "Gemfile", "Gemfile.lock", "LICENSE", "Procfile", "Procfile.production", "README.md", "Rakefile", "VERSION", "app", "config.ru", "config", "db", "doc", "lib", "log", "public", "resque.sh", "script", "spec", "vendor"] FILES = [".foreman", ".gitignore", ".rails_footnotes", ".rspec", ".travis.yml", "CHANGELOG", "Gemfile", "Gemfile.lock", "LICENSE", "Procfile", "Procfile.production", "README.md", "Rakefile", "VERSION", "app", "config.ru", "config", "db", "doc", "lib", "log", "public", "resque.sh", "script", "spec", "vendor"]
FILES_COUNT = 26 FILES_COUNT = 26
......
module ValidCommitWithAltEmail
ID = "1e689bfba39525ead225eaf611948cfbe8ac34cf"
MESSAGE = "fixed notes logic"
AUTHOR_FULL_NAME = "Dmitriy Zaporozhets"
AUTHOR_EMAIL = "dzaporozhets@sphereconsultinginc.com"
end
\ No newline at end of file
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