Commit e6ce4729 authored by Alex Denisov's avatar Alex Denisov

master merged

parents 77bfc591 61049424
## Contribute to GitLab
If you want to contribute to GitLab, follow this process:
1. Fork the project
2. Create a feature branch
3. Code
4. Create a pull request
We only accept pull requests if:
* Your code has proper tests and all tests pass
* Your code can be merged w/o problems
* It wont broke existing functionality
* Its a quality code
* We like it :)
## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
## Running tests
To run the specs for GitLab, you need to run seeds for test db.
cd gitlabhq
rake db:seed_fu RAILS_ENV=test
Then you can run the test suite with rake:
rake gitlab:test
source "http://rubygems.org" source "http://rubygems.org"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
end
def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "3.2.8" gem "rails", "3.2.8"
# Supported DBs # Supported DBs
...@@ -8,6 +16,10 @@ gem "mysql2" ...@@ -8,6 +16,10 @@ gem "mysql2"
# Auth # Auth
gem "devise", "~> 2.1.0" gem "devise", "~> 2.1.0"
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
# GITLAB patched libs # GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
...@@ -98,21 +110,28 @@ group :development do ...@@ -98,21 +110,28 @@ group :development do
end end
group :development, :test do group :development, :test do
gem 'spinach-rails'
gem "rspec-rails" gem "rspec-rails"
gem "capybara" gem "capybara"
gem "capybara-webkit" gem "capybara-webkit"
gem "headless" gem "headless"
gem "autotest"
gem "autotest-rails"
gem "pry" gem "pry"
gem "awesome_print" gem "awesome_print"
gem "database_cleaner" gem "database_cleaner"
gem "launchy" gem "launchy"
gem 'factory_girl_rails' gem 'factory_girl_rails'
# Guard
gem 'guard-rspec'
gem 'guard-spinach'
# Notification
gem 'rb-fsevent', :require => darwin_only('rb-fsevent')
gem 'growl', :require => darwin_only('growl')
gem 'rb-inotify', :require => linux_only('rb-inotify')
end end
group :test do group :test do
gem 'cucumber-rails', :require => false
gem "simplecov", :require => false gem "simplecov", :require => false
gem "shoulda-matchers" gem "shoulda-matchers"
gem 'email_spec' gem 'email_spec'
......
...@@ -68,7 +68,6 @@ GIT ...@@ -68,7 +68,6 @@ GIT
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
ZenTest (4.8.1)
actionmailer (3.2.8) actionmailer (3.2.8)
actionpack (= 3.2.8) actionpack (= 3.2.8)
mail (~> 2.4.4) mail (~> 2.4.4)
...@@ -100,10 +99,6 @@ GEM ...@@ -100,10 +99,6 @@ GEM
rails (~> 3.0) rails (~> 3.0)
addressable (2.2.8) addressable (2.2.8)
arel (3.0.2) arel (3.0.2)
autotest (4.4.6)
ZenTest (>= 4.4.1)
autotest-rails (4.1.2)
ZenTest (~> 4.5)
awesome_print (1.0.2) awesome_print (1.0.2)
bcrypt-ruby (3.0.1) bcrypt-ruby (3.0.1)
blankslate (2.1.2.4) blankslate (2.1.2.4)
...@@ -137,16 +132,8 @@ GEM ...@@ -137,16 +132,8 @@ GEM
execjs execjs
coffee-script-source (1.3.3) coffee-script-source (1.3.3)
colored (1.2) colored (1.2)
colorize (0.5.8)
crack (0.3.1) crack (0.3.1)
cucumber (1.2.1)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.11.0)
json (>= 1.4.6)
cucumber-rails (1.3.0)
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
nokogiri (>= 1.5.0)
daemons (1.1.8) daemons (1.1.8)
database_cleaner (0.8.0) database_cleaner (0.8.0)
devise (2.1.2) devise (2.1.2)
...@@ -171,12 +158,13 @@ GEM ...@@ -171,12 +158,13 @@ GEM
factory_girl_rails (4.0.0) factory_girl_rails (4.0.0)
factory_girl (~> 4.0.0) factory_girl (~> 4.0.0)
railties (>= 3.0.0) railties (>= 3.0.0)
faraday (0.8.4)
multipart-post (~> 1.1)
ffaker (1.14.0) ffaker (1.14.0)
ffi (1.0.11) ffi (1.0.11)
foreman (0.47.0) foreman (0.47.0)
thor (>= 0.13.6) thor (>= 0.13.6)
gherkin (2.11.0) gherkin-ruby (0.2.1)
json (>= 1.4.6)
git (1.2.5) git (1.2.5)
github-markup (0.7.4) github-markup (0.7.4)
gitlab_meta (2.9) gitlab_meta (2.9)
...@@ -186,6 +174,15 @@ GEM ...@@ -186,6 +174,15 @@ GEM
multi_xml multi_xml
rack rack
rack-mount rack-mount
growl (1.0.3)
guard (1.3.2)
listen (>= 0.4.2)
thor (>= 0.14.6)
guard-rspec (1.2.1)
guard (>= 1.1)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
haml (3.1.6) haml (3.1.6)
haml-rails (0.3.4) haml-rails (0.3.4)
actionpack (~> 3.0) actionpack (~> 3.0)
...@@ -199,6 +196,7 @@ GEM ...@@ -199,6 +196,7 @@ GEM
httparty (0.8.3) httparty (0.8.3)
multi_json (~> 1.0) multi_json (~> 1.0)
multi_xml multi_xml
httpauth (0.1)
i18n (0.6.1) i18n (0.6.1)
journey (1.0.4) journey (1.0.4)
jquery-rails (2.0.2) jquery-rails (2.0.2)
...@@ -208,6 +206,8 @@ GEM ...@@ -208,6 +206,8 @@ GEM
jquery-rails jquery-rails
railties (>= 3.1.0) railties (>= 3.1.0)
json (1.7.5) json (1.7.5)
jwt (0.1.5)
multi_json (>= 1.0)
kaminari (0.14.0) kaminari (0.14.0)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
...@@ -219,6 +219,7 @@ GEM ...@@ -219,6 +219,7 @@ GEM
libv8 (3.3.10.4) libv8 (3.3.10.4)
libwebsocket (0.1.3) libwebsocket (0.1.3)
addressable addressable
listen (0.5.0)
mail (2.4.4) mail (2.4.4)
i18n (>= 0.4.0) i18n (>= 0.4.0)
mime-types (~> 1.16) mime-types (~> 1.16)
...@@ -229,12 +230,35 @@ GEM ...@@ -229,12 +230,35 @@ GEM
sprockets (~> 2.0) sprockets (~> 2.0)
multi_json (1.3.6) multi_json (1.3.6)
multi_xml (0.5.1) multi_xml (0.5.1)
multipart-post (1.1.5)
mysql2 (0.3.11) mysql2 (0.3.11)
net-ldap (0.2.2) net-ldap (0.2.2)
nokogiri (1.5.3) nokogiri (1.5.3)
oauth (0.4.7)
oauth2 (0.8.0)
faraday (~> 0.8)
httpauth (~> 0.1)
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
omniauth (1.1.0) omniauth (1.1.0)
hashie (~> 1.2) hashie (~> 1.2)
rack rack
omniauth-github (1.0.3)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-google-oauth2 (0.1.13)
omniauth (~> 1.0)
omniauth-oauth2
omniauth-oauth (1.0.1)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.1.0)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
omniauth-twitter (0.0.13)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
orm_adapter (0.3.0) orm_adapter (0.3.0)
polyglot (0.3.3) polyglot (0.3.3)
posix-spawn (0.3.6) posix-spawn (0.3.6)
...@@ -274,6 +298,9 @@ GEM ...@@ -274,6 +298,9 @@ GEM
raindrops (0.9.0) raindrops (0.9.0)
rake (0.9.2.2) rake (0.9.2.2)
raphael-rails (1.5.2) raphael-rails (1.5.2)
rb-fsevent (0.9.1)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
rdoc (3.12) rdoc (3.12)
json (~> 1.4) json (~> 1.4)
redcarpet (2.1.1) redcarpet (2.1.1)
...@@ -336,6 +363,13 @@ GEM ...@@ -336,6 +363,13 @@ GEM
tilt (~> 1.3, >= 1.3.3) tilt (~> 1.3, >= 1.3.3)
six (0.2.0) six (0.2.0)
slop (2.4.4) slop (2.4.4)
spinach (0.5.2)
colorize
gherkin-ruby (~> 0.2.0)
spinach-rails (0.1.8)
capybara (~> 1)
railties (>= 3)
spinach (>= 0.4)
sprockets (2.1.3) sprockets (2.1.3)
hike (~> 1.2) hike (~> 1.2)
rack (~> 1.0) rack (~> 1.0)
...@@ -378,8 +412,6 @@ PLATFORMS ...@@ -378,8 +412,6 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
acts-as-taggable-on (= 2.3.1) acts-as-taggable-on (= 2.3.1)
annotate! annotate!
autotest
autotest-rails
awesome_print awesome_print
bootstrap-sass (= 2.0.4) bootstrap-sass (= 2.0.4)
capybara capybara
...@@ -389,7 +421,6 @@ DEPENDENCIES ...@@ -389,7 +421,6 @@ DEPENDENCIES
chosen-rails chosen-rails
coffee-rails (= 3.2.2) coffee-rails (= 3.2.2)
colored colored
cucumber-rails
database_cleaner database_cleaner
devise (~> 2.1.0) devise (~> 2.1.0)
draper draper
...@@ -404,6 +435,9 @@ DEPENDENCIES ...@@ -404,6 +435,9 @@ DEPENDENCIES
grack! grack!
grape (~> 0.2.1) grape (~> 0.2.1)
grit! grit!
growl
guard-rspec
guard-spinach
haml-rails haml-rails
headless headless
httparty httparty
...@@ -415,12 +449,18 @@ DEPENDENCIES ...@@ -415,12 +449,18 @@ DEPENDENCIES
linguist (~> 1.0.0)! linguist (~> 1.0.0)!
modernizr (= 2.5.3) modernizr (= 2.5.3)
mysql2 mysql2
omniauth
omniauth-github
omniauth-google-oauth2
omniauth-ldap! omniauth-ldap!
omniauth-twitter
pry pry
pygments.rb! pygments.rb!
rack-mini-profiler rack-mini-profiler
rails (= 3.2.8) rails (= 3.2.8)
raphael-rails (= 1.5.2) raphael-rails (= 1.5.2)
rb-fsevent
rb-inotify
redcarpet (~> 2.1.1) redcarpet (~> 2.1.1)
resque (~> 1.20.0) resque (~> 1.20.0)
resque_mailer resque_mailer
...@@ -432,6 +472,7 @@ DEPENDENCIES ...@@ -432,6 +472,7 @@ DEPENDENCIES
shoulda-matchers shoulda-matchers
simplecov simplecov
six six
spinach-rails
sqlite3 sqlite3
stamp stamp
test_after_commit test_after_commit
......
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
end
guard 'spinach' do
watch(%r|^features/(.*)\.feature|)
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
"features/#{m[1]}#{m[2]}.feature"
end
end
...@@ -6,3 +6,7 @@ $ -> ...@@ -6,3 +6,7 @@ $ ->
elems.val('').attr 'disabled', true elems.val('').attr 'disabled', true
else else
elems.removeAttr 'disabled' elems.removeAttr 'disabled'
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
//= require jquery.endless-scroll //= require jquery.endless-scroll
//= require jquery.highlight //= require jquery.highlight
//= require jquery.waitforimages //= require jquery.waitforimages
//= require bootstrap-modal //= require bootstrap
//= require modernizr //= require modernizr
//= require chosen-jquery //= require chosen-jquery
//= require raphael //= require raphael
......
...@@ -24,6 +24,9 @@ $ -> ...@@ -24,6 +24,9 @@ $ ->
# Click a .one_click_select field, select the contents # Click a .one_click_select field, select the contents
$(".one_click_select").live 'click', -> $(this).select() $(".one_click_select").live 'click', -> $(this).select()
# Initialize chosen selects
$('select.chosen').chosen()
# Disable form buttons while a form is submitting # Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', this) buttons = $('[type="submit"]', this)
......
var NoteList = {
notes_path: null,
target_params: null,
target_id: 0,
target_type: null,
first_id: 0,
last_id: 0,
disable:false,
init:
function(tid, tt, path) {
this.notes_path = path + ".js";
this.target_id = tid;
this.target_type = tt;
this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
// get notes
this.getContent();
// get new notes every n seconds
this.initRefresh();
$('.delete-note').live('ajax:success', function() {
$(this).closest('li').fadeOut(); });
$(".note-form-holder").live("ajax:before", function(){
$(".submit_note").disable()
})
$(".note-form-holder").live("ajax:complete", function(){
$(".submit_note").enable()
})
disableButtonIfEmptyField(".note-text", ".submit_note");
$(".note-text").live("focus", function(){
$(this).css("height", "80px");
$('.note_advanced_opts').show();
});
$("#note_attachment").change(function(e){
var val = $('.input-file').val();
var filename = val.replace(/^.*[\\\/]/, '');
$(".file_name").text(filename);
});
},
/**
* Load new notes to fresh list called 'new_notes_list':
* - Replace 'new_notes_list' with new list every n seconds
* - Append new notes to this list after submit
*/
initRefresh:
function() {
// init timer
var intNew = setInterval("NoteList.getNew()", 10000);
},
replace:
function(html) {
$("#new_notes_list").html(html);
},
prepend:
function(id, html) {
if(id != this.last_id) {
$("#new_notes_list").prepend(html);
}
},
getNew:
function() {
// refersh notes list
$.ajax({
type: "GET",
url: this.notes_path,
data: "last_id=" + this.last_id + this.target_params,
dataType: "script"});
},
refresh:
function() {
// refersh notes list
$.ajax({
type: "GET",
url: this.notes_path,
data: "first_id=" + this.first_id + "&last_id=" + this.last_id + this.target_params,
dataType: "script"});
},
/**
* Init load of notes:
* 1. Get content with ajax call
* 2. Set content of notes list with loaded one
*/
getContent:
function() {
$.ajax({
type: "GET",
url: this.notes_path,
data: "?" + this.target_params,
complete: function(){ $('.status').removeClass("loading")},
beforeSend: function() { $('.status').addClass("loading") },
dataType: "script"});
},
setContent:
function(fid, lid, html) {
this.last_id = lid;
this.first_id = fid;
$("#notes-list").html(html);
// Init infinite scrolling
this.initLoadMore();
},
/**
* Paging for old notes when scroll to bottom:
* 1. Init scroll events with 'initLoadMore'
* 2. Load onlder notes with 'getOld' method
* 3. append old notes to bottom of list with 'append'
*
*/
getOld:
function() {
$('.loading').show();
$.ajax({
type: "GET",
url: this.notes_path,
data: "first_id=" + this.first_id + this.target_params,
complete: function(){ $('.status').removeClass("loading")},
beforeSend: function() { $('.status').addClass("loading") },
dataType: "script"});
},
append:
function(id, html) {
if(this.first_id == id) {
this.disable = true;
} else {
this.first_id = id;
$("#notes-list").append(html);
}
},
initLoadMore:
function() {
$(document).endlessScroll({
bottomPixels: 400,
fireDelay: 1000,
fireOnce:true,
ceaseFire: function() {
return NoteList.disable;
},
callback: function(i) {
NoteList.getOld();
}
});
}
};
var PerLineNotes = {
init:
function() {
$(".line_note_link, .line_note_reply_link").live("click", function(e) {
var form = $(".per_line_form");
$(this).closest("tr").after(form);
form.find("#note_line_code").val($(this).attr("line_code"));
form.show();
return false;
});
disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
}
}
var NoteList = {
notes_path: null,
target_params: null,
target_id: 0,
target_type: null,
top_id: 0,
bottom_id: 0,
loading_more_disabled: false,
reversed: false,
init:
function(tid, tt, path) {
this.notes_path = path + ".js";
this.target_id = tid;
this.target_type = tt;
this.reversed = $("#notes-list").hasClass("reversed");
this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
// get initial set of notes
this.getContent();
$("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() {
$(this).closest('li').fadeOut(function() {
$(this).remove();
NoteList.updateVotes();
});
});
$(".note-form-holder").on("ajax:before", function(){
$(".submit_note").disable();
})
$(".note-form-holder").on("ajax:complete", function(){
$(".submit_note").enable();
})
disableButtonIfEmptyField(".note-text", ".submit_note");
$("#note_attachment").change(function(e){
var val = $('.input-file').val();
var filename = val.replace(/^.*[\\\/]/, '');
$(".file_name").text(filename);
});
if(this.reversed) {
var textarea = $(".note-text");
$('.note_advanced_opts').hide();
textarea.css("height", "40px");
textarea.on("focus", function(){
$(this).css("height", "80px");
$('.note_advanced_opts').show();
});
}
},
/**
* Handle loading the initial set of notes.
* And set up loading more notes when scrolling to the bottom of the page.
*/
/**
* Gets an inital set of notes.
*/
getContent:
function() {
$.ajax({
type: "GET",
url: this.notes_path,
data: "?" + this.target_params,
complete: function(){ $('.notes-status').removeClass("loading")},
beforeSend: function() { $('.notes-status').addClass("loading") },
dataType: "script"});
},
/**
* Called in response to getContent().
* Replaces the content of #notes-list with the given html.
*/
setContent:
function(first_id, last_id, html) {
this.top_id = first_id;
this.bottom_id = last_id;
$("#notes-list").html(html);
// init infinite scrolling
this.initLoadMore();
// init getting new notes
if (this.reversed) {
this.initRefreshNew();
}
},
/**
* Handle loading more notes when scrolling to the bottom of the page.
* The id of the last note in the list is in this.bottom_id.
*
* Set up refreshing only new notes after all notes have been loaded.
*/
/**
* Initializes loading more notes when scrolling to the bottom of the page.
*/
initLoadMore:
function() {
$(document).endlessScroll({
bottomPixels: 400,
fireDelay: 1000,
fireOnce:true,
ceaseFire: function() {
return NoteList.loading_more_disabled;
},
callback: function(i) {
NoteList.getMore();
}
});
},
/**
* Gets an additional set of notes.
*/
getMore:
function() {
// only load more notes if there are no "new" notes
$('.loading').show();
$.ajax({
type: "GET",
url: this.notes_path,
data: "loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id + this.target_params,
complete: function(){ $('.notes-status').removeClass("loading")},
beforeSend: function() { $('.notes-status').addClass("loading") },
dataType: "script"});
},
/**
* Called in response to getMore().
* Append notes to #notes-list.
*/
appendMoreNotes:
function(id, html) {
if(id != this.bottom_id) {
this.bottom_id = id;
$("#notes-list").append(html);
}
},
/**
* Called in response to getMore().
* Disables loading more notes when scrolling to the bottom of the page.
* Initalizes refreshing new notes.
*/
finishedLoadingMore:
function() {
this.loading_more_disabled = true;
// from now on only get new notes
if (!this.reversed) {
this.initRefreshNew();
}
// make sure we are up to date
this.updateVotes();
},
/**
* Handle refreshing and adding of new notes.
*
* New notes are all notes that are created after the site has been loaded.
* The "old" notes are in #notes-list the "new" ones will be in #new-notes-list.
* The id of the last "old" note is in this.bottom_id.
*/
/**
* Initializes getting new notes every n seconds.
*/
initRefreshNew:
function() {
setInterval("NoteList.getNew()", 10000);
},
/**
* Gets the new set of notes.
*/
getNew:
function() {
$.ajax({
type: "GET",
url: this.notes_path,
data: "loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id) + this.target_params,
dataType: "script"});
},
/**
* Called in response to getNew().
* Replaces the content of #new-notes-list with the given html.
*/
replaceNewNotes:
function(html) {
$("#new-notes-list").html(html);
this.updateVotes();
},
/**
* Adds a single note to #new-notes-list.
*/
appendNewNote:
function(id, html) {
if (this.reversed) {
$("#new-notes-list").prepend(html);
} else {
$("#new-notes-list").append(html);
}
this.updateVotes();
},
/**
* Recalculates the votes and updates them (if they are displayed at all).
*
* Assumes all relevant notes are displayed (i.e. there are no more notes to
* load via getMore()).
* Might produce inaccurate results when not all notes have been loaded and a
* recalculation is triggered (e.g. when deleting a note).
*/
updateVotes:
function() {
var votes = $("#votes .votes");
var notes = $("#notes-list, #new-notes-list").find(".note.vote");
// only update if there is a vote display
if (votes.size()) {
var upvotes = notes.filter(".upvote").size();
var downvotes = notes.filter(".downvote").size();
var votesCount = upvotes + downvotes;
var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0;
var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0;
// change vote bar lengths
votes.find(".bar-success").css("width", upvotesPercent+"%");
votes.find(".bar-danger").css("width", downvotesPercent+"%");
// replace vote numbers
votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes));
votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes));
}
}
};
var PerLineNotes = {
init:
function() {
/**
* Called when clicking on the "add note" or "reply" button for a diff line.
*
* Shows the note form below the line.
* Sets some hidden fields in the form.
*/
$(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) {
var form = $(".per_line_form");
$(this).closest("tr").after(form);
form.find("#note_line_code").val($(this).data("lineCode"));
form.show();
return false;
});
disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
/**
* Called in response to successfully deleting a note on a diff line.
*
* Removes the actual note from view.
* Removes the reply button if the last note for that line has been removed.
*/
$(".diff_file_content").on("ajax:success", ".delete-note", function() {
var trNote = $(this).closest("tr");
trNote.fadeOut(function() {
$(this).remove();
});
// check if this is the last note for this line
// elements must really be removed for this to work reliably
var trLine = trNote.prev();
var trRpl = trNote.next();
if (trLine.hasClass("line_holder") && trRpl.hasClass("reply")) {
trRpl.fadeOut(function() { $(this).remove(); });
}
});
}
}
...@@ -10,11 +10,15 @@ window.Projects = -> ...@@ -10,11 +10,15 @@ window.Projects = ->
$('form #project_default_branch').chosen() $('form #project_default_branch').chosen()
disableButtonIfEmptyField '#project_name', '.project-submit' disableButtonIfEmptyField '#project_name', '.project-submit'
# Git clone panel switcher
$ -> $ ->
# Git clone panel switcher
scope = $ '.project_clone_holder' scope = $ '.project_clone_holder'
if scope.length > 0 if scope.length > 0
$('a, button', scope).click -> $('a, button', scope).click ->
$('a, button', scope).removeClass 'active' $('a, button', scope).removeClass 'active'
$(@).addClass 'active' $(@).addClass 'active'
$('#project_clone', scope).val $(@).data 'clone' $('#project_clone', scope).val $(@).data 'clone'
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
...@@ -145,6 +145,19 @@ span.update-author { ...@@ -145,6 +145,19 @@ span.update-author {
.label { .label {
background-color: #474D57; background-color: #474D57;
&.label-tag {
background: none;
border: none;
padding:4px 6px;
color:#444;
text-shadow:0 0 1px #fff;
&.grouped {
float: left;
margin-right: 6px;
padding: 6px;
}
}
&.label-issue { &.label-issue {
background-color: #eee; background-color: #eee;
border: 1px solid #ccc; border: 1px solid #ccc;
...@@ -158,6 +171,18 @@ span.update-author { ...@@ -158,6 +171,18 @@ span.update-author {
padding: 6px; padding: 6px;
} }
} }
&.label-success {
background-color: #8D8;
color: #333;
text-shadow: 0 1px 1px white;
}
&.label-error {
background-color: #D88;
color: #333;
text-shadow: 0 1px 1px white;
}
} }
.event_label { .event_label {
...@@ -181,11 +206,12 @@ span.update-author { ...@@ -181,11 +206,12 @@ span.update-author {
} }
&.joined { &.joined {
background-color: #1cb9ff; background-color: #1ca9dd;
} }
&.left { &.left {
background-color: #ff5057; background-color: #888;
float:none;
} }
} }
...@@ -414,13 +440,48 @@ p.time { ...@@ -414,13 +440,48 @@ p.time {
} }
} }
.upvotes { .votes {
font-size: 14px; font-size: 13px;
font-weight: bold; line-height: 15px;
.progress {
height: 4px;
margin: 0;
.bar {
float: left;
height: 100%;
}
.bar-success {
background-color: #468847;
@include bg-gradient(#62C462, #51A351);
}
.bar-danger {
background-color: #B94A48;
@include bg-gradient(#EE5F5B, #BD362F);
}
}
.upvotes {
display: inline-block;
color: #468847; color: #468847;
text-align: right; }
padding: 4px; .downvotes {
margin: 2px; display: inline-block;
color: #B94A48;
}
}
.votes-block {
margin: 14px 6px 6px 0;
.downvotes {
float: right;
}
}
.votes-inline {
display: inline-block;
margin: 0 8px;
.progress {
display: inline-block;
padding: 0 0 2px;
width: 45px;
}
} }
/* Fix for readme code (stopped it from being yellow) */ /* Fix for readme code (stopped it from being yellow) */
...@@ -624,7 +685,7 @@ li.note { ...@@ -624,7 +685,7 @@ li.note {
margin-right:40px; margin-right:40px;
.prev { .prev {
@extend .borders; @extend .thumbnail;
height:120px; height:120px;
width:175px; width:175px;
margin-bottom:10px; margin-bottom:10px;
...@@ -653,3 +714,31 @@ li.note { ...@@ -653,3 +714,31 @@ li.note {
text-align:center; text-align:center;
margin-bottom:10px; margin-bottom:10px;
} }
.oauth_select_holder {
padding:20px;
img {
padding:5px;
margin-right:10px;
}
.active {
img {
border:1px solid #ccc;
background:$hover;
@include border-radius(5px);
}
}
}
.btn-build-token {
float: left;
padding: 6px 20px;
margin-right: 12px;
}
.gitlab-promo {
a {
color:#aaa;
margin-right: 30px;
}
}
...@@ -65,6 +65,10 @@ ...@@ -65,6 +65,10 @@
border-color: #CCC; border-color: #CCC;
@include solid_shade; @include solid_shade;
&.white {
background:#fff;
}
ul { ul {
margin:0; margin:0;
} }
...@@ -142,4 +146,8 @@ ...@@ -142,4 +146,8 @@
border:none; border:none;
} }
} }
.ui-box-body {
padding:10px;
}
} }
...@@ -33,7 +33,29 @@ ...@@ -33,7 +33,29 @@
.nav-pills a:hover { background-color:#888; } .nav-pills a:hover { background-color:#888; }
.nav-pills .active a { background-color: $style_color; } .nav-pills .active a { background-color: $style_color; }
.nav-tabs > li > a, .nav-pills > li > a { color:$style_color; } .nav-tabs > li > a, .nav-pills > li > a { color:$style_color; }
.nav-tabs > .active > a { font-weight:bold; } .nav.nav-tabs {
li {
> a {
padding:8px 20px;
margin-right: 7px;
border-color: #EEE;
color:#888;
border-bottom: 1px solid #ddd;
.badge {
background-color: #eee;
color:#888;
text-shadow:0 1px 1px #fff;
}
}
&.active {
> a {
border-color: #CCC;
border-bottom: 1px solid #fff;
color:#333;
}
}
}
}
/** ALERT MESSAGES **/ /** ALERT MESSAGES **/
.alert-message { @extend .alert; } .alert-message { @extend .alert; }
...@@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; } ...@@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; }
/** HELPERS **/ /** HELPERS **/
.nothing_here_message { text-align:center; padding:20px; color:#777; } .nothing_here_message { text-align:center; padding:20px; color:#777; }
p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; } p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; }
/** FORMS **/
input[type='search'].search-text-input {
background-image: url("icon-search.png");
background-repeat: no-repeat;
background-position: 10px;
padding-left:25px;
@include border-radius(4px);
border:1px solid #ccc;
}
...@@ -135,7 +135,6 @@ $hover: #fdf5d9; ...@@ -135,7 +135,6 @@ $hover: #fdf5d9;
*/ */
@import "common.scss"; @import "common.scss";
/** /**
* Styles related to specific part of app * Styles related to specific part of app
*/ */
...@@ -161,6 +160,11 @@ $hover: #fdf5d9; ...@@ -161,6 +160,11 @@ $hover: #fdf5d9;
*/ */
@import "sections/notes.scss"; @import "sections/notes.scss";
/**
* This file represent profile styles
*/
@import "sections/profile.scss";
/** /**
* Devise styles * Devise styles
*/ */
......
...@@ -20,15 +20,25 @@ ...@@ -20,15 +20,25 @@
.chzn-drop { .chzn-drop {
margin:7px 0; margin:7px 0;
border: 1px solid #CCC; min-width: 400px;
min-width: 300px; border: 2px solid $blue_link;
@include border-radius(4px);
.chzn-results { .chzn-results {
max-height:300px; max-height:300px;
.group-result {
color: $blue_link;
}
.active-result {
&.highlighted {
background: $blue_link;
}
}
} }
.chzn-search input { .chzn-search input {
min-width:200px; min-width:365px;
} }
} }
......
...@@ -105,6 +105,7 @@ input.check_all_issues { ...@@ -105,6 +105,7 @@ input.check_all_issues {
width:100px; width:100px;
} }
/** /**
* Milestones list * Milestones list
* *
......
...@@ -88,11 +88,10 @@ li.merge_request { ...@@ -88,11 +88,10 @@ li.merge_request {
@include round-borders-all(4px); @include round-borders-all(4px);
padding:2px 4px; padding:2px 4px;
border:none; border:none;
font-size:13px; font-size:14px;
background: #474D57; background: #474D57;
color:#fff; color:#fff;
font-weight:bold; font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
font-family: monospace;
} }
.mr_source_commit, .mr_source_commit,
......
...@@ -55,7 +55,6 @@ ul.main_menu { ...@@ -55,7 +55,6 @@ ul.main_menu {
&.current { &.current {
background-color:#D5D5D5; background-color:#D5D5D5;
border-bottom: 1px solid #AAA;
border-right: 1px solid #BBB; border-right: 1px solid #BBB;
border-left: 1px solid #BBB; border-left: 1px solid #BBB;
border-radius: 0 0 1px 1px; border-radius: 0 0 1px 1px;
......
...@@ -3,17 +3,13 @@ ...@@ -3,17 +3,13 @@
* *
*/ */
#notes-list, #notes-list,
#new_notes_list { #new-notes-list {
display:block; display:block;
list-style:none; list-style:none;
margin:0px; margin:0px;
padding:0px; padding:0px;
} }
#new_notes_list li:last-child{
border-bottom:1px solid #aaa;
}
.issue_notes, .issue_notes,
.wiki_notes { .wiki_notes {
.note_content { .note_content {
...@@ -30,9 +26,6 @@ ...@@ -30,9 +26,6 @@
} }
#new_note { #new_note {
.note-text {
height:40px;
}
.attach_holder { .attach_holder {
display:none; display:none;
} }
...@@ -48,7 +41,6 @@ ...@@ -48,7 +41,6 @@
.note { .note {
padding: 8px 0; padding: 8px 0;
border-bottom: 1px solid #eee;
overflow: hidden; overflow: hidden;
display: block; display: block;
img {float: left; margin-right: 10px;} img {float: left; margin-right: 10px;}
...@@ -70,6 +62,23 @@ ...@@ -70,6 +62,23 @@
.delete-note { display:block; } .delete-note { display:block; }
} }
} }
#notes-list:not(.reversed) .note,
#new-notes-list:not(.reversed) .note {
border-bottom: 1px solid #eee;
}
#notes-list.reversed .note,
#new-notes-list.reversed .note {
border-top: 1px solid #eee;
}
/* mark vote notes */
.voting_notes .note {
padding: 8px 0;
}
.notes-status {
margin: 18px;
}
p.notify_controls input{ p.notify_controls input{
......
.profile_history {
.event_feed {
min-height:20px;
.avatar {
width:20px;
}
}
}
...@@ -3,29 +3,30 @@ module Notes ...@@ -3,29 +3,30 @@ module Notes
def execute def execute
target_type = params[:target_type] target_type = params[:target_type]
target_id = params[:target_id] target_id = params[:target_id]
first_id = params[:first_id] after_id = params[:after_id]
last_id = params[:last_id] before_id = params[:before_id]
@notes = case target_type @notes = case target_type
when "commit" when "commit"
then project.commit_notes(project.commit(target_id)).fresh.limit(20) project.commit_notes(project.commit(target_id)).fresh.limit(20)
when "snippet"
then project.snippets.find(target_id).notes
when "wall"
then project.common_notes.order("created_at DESC").fresh.limit(50)
when "issue" when "issue"
then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20) project.issues.find(target_id).notes.inc_author.fresh.limit(20)
when "merge_request" when "merge_request"
then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20) project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20)
when "snippet"
project.snippets.find(target_id).notes.fresh
when "wall"
# this is the only case, where the order is DESC
project.common_notes.order("created_at DESC, id DESC").limit(50)
when "wiki" when "wiki"
then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20] project.wiki_notes.limit(20)
end end
@notes = if last_id @notes = if after_id
@notes.where("id > ?", last_id) @notes.where("id > ?", after_id)
elsif first_id elsif before_id
@notes.where("id < ?", first_id) @notes.where("id < ?", before_id)
else else
@notes @notes
end end
......
class Admin::DashboardController < ApplicationController class Admin::DashboardController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index def index
@workers = Resque.workers @workers = Resque.workers
@pending_jobs = Resque.size(:post_receive) @pending_jobs = Resque.size(:post_receive)
......
class Admin::HooksController < ApplicationController class Admin::HooksController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index def index
@hooks = SystemHook.all @hooks = SystemHook.all
@hook = SystemHook.new @hook = SystemHook.new
......
class Admin::LogsController < ApplicationController class Admin::LogsController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
end end
class Admin::ProjectsController < ApplicationController class Admin::ProjectsController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update] before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update]
def index def index
......
class Admin::ResqueController < ApplicationController class Admin::ResqueController < AdminController
layout 'admin'
def show def show
end end
end end
class Admin::TeamMembersController < ApplicationController class Admin::TeamMembersController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def edit def edit
@admin_team_member = UsersProject.find(params[:id]) @admin_team_member = UsersProject.find(params[:id])
end end
......
class Admin::UsersController < ApplicationController class Admin::UsersController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index def index
@admin_users = User.scoped @admin_users = User.scoped
@admin_users = @admin_users.filter(params[:filter]) @admin_users = @admin_users.filter(params[:filter])
......
# Provides a base class for Admin controllers to subclass
#
# Automatically sets the layout and ensures an administrator is logged in
class AdminController < ApplicationController
layout 'admin'
before_filter :authenticate_admin!
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
end
...@@ -84,10 +84,6 @@ class ApplicationController < ActionController::Base ...@@ -84,10 +84,6 @@ class ApplicationController < ActionController::Base
abilities << Ability abilities << Ability
end end
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_project!(action) def authorize_project!(action)
return access_denied! unless can?(current_user, action, project) return access_denied! unless can?(current_user, action, project)
end end
......
...@@ -64,7 +64,7 @@ class CommitsController < ApplicationController ...@@ -64,7 +64,7 @@ class CommitsController < ApplicationController
@commit.to_patch, @commit.to_patch,
type: "text/plain", type: "text/plain",
disposition: 'attachment', disposition: 'attachment',
filename: "#{@commit.id.patch}" filename: "#{@commit.id}.patch"
) )
end end
......
...@@ -17,7 +17,7 @@ class IssuesController < ApplicationController ...@@ -17,7 +17,7 @@ class IssuesController < ApplicationController
before_filter :authorize_write_issue!, only: [:new, :create] before_filter :authorize_write_issue!, only: [:new, :create]
# Allow modify issue # Allow modify issue
before_filter :authorize_modify_issue!, only: [:close, :edit, :update] before_filter :authorize_modify_issue!, only: [:edit, :update]
# Allow destroy issue # Allow destroy issue
before_filter :authorize_admin_issue!, only: [:destroy] before_filter :authorize_admin_issue!, only: [:destroy]
...@@ -87,8 +87,6 @@ class IssuesController < ApplicationController ...@@ -87,8 +87,6 @@ class IssuesController < ApplicationController
end end
def destroy def destroy
return access_denied! unless can?(current_user, :admin_issue, @issue)
@issue.destroy @issue.destroy
respond_to do |format| respond_to do |format|
......
class OmniauthCallbacksController < Devise::OmniauthCallbacksController class OmniauthCallbacksController < Devise::OmniauthCallbacksController
Gitlab.config.omniauth_providers.each do |provider|
define_method provider['name'] do
handle_omniauth
end
end
# Extend the standard message generation to accept our custom exception # Extend the standard message generation to accept our custom exception
def failure_message def failure_message
...@@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
sign_in_and_redirect @user sign_in_and_redirect @user
end end
private
def handle_omniauth
oauth = request.env['omniauth.auth']
provider, uid = oauth['provider'], oauth['uid']
if current_user
# Change a logged-in user's authentication method:
current_user.extern_uid = uid
current_user.provider = provider
current_user.save
redirect_to profile_path
else
@user = User.find_or_new_for_omniauth(oauth)
if @user
sign_in_and_redirect @user
else
flash[:notice] = "There's no such user!"
redirect_to new_user_session_path
end
end
end
end end
...@@ -16,9 +16,6 @@ class ProfileController < ApplicationController ...@@ -16,9 +16,6 @@ class ProfileController < ApplicationController
def token def token
end end
def password
end
def password_update def password_update
params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
...@@ -32,7 +29,11 @@ class ProfileController < ApplicationController ...@@ -32,7 +29,11 @@ class ProfileController < ApplicationController
def reset_private_token def reset_private_token
current_user.reset_authentication_token! current_user.reset_authentication_token!
redirect_to profile_token_path redirect_to profile_account_path
end
def history
@events = current_user.recent_events.page(params[:page]).per(20)
end end
private private
......
...@@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController ...@@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController
# Authorize # Authorize
before_filter :add_project_abilities before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_admin_project!, except: [:show] before_filter :authorize_admin_project!, except: [:index, :show]
def index
end
def show def show
@team_member = project.users_projects.find(params[:id]) @team_member = project.users_projects.find(params[:id])
...@@ -22,7 +25,7 @@ class TeamMembersController < ApplicationController ...@@ -22,7 +25,7 @@ class TeamMembersController < ApplicationController
params[:project_access] params[:project_access]
) )
redirect_to team_project_path(@project) redirect_to project_team_index_path(@project)
end end
def update def update
...@@ -32,7 +35,7 @@ class TeamMembersController < ApplicationController ...@@ -32,7 +35,7 @@ class TeamMembersController < ApplicationController
unless @team_member.valid? unless @team_member.valid?
flash[:alert] = "User should have at least one role" flash[:alert] = "User should have at least one role"
end end
redirect_to team_project_path(@project) redirect_to project_team_index_path(@project)
end end
def destroy def destroy
...@@ -40,7 +43,7 @@ class TeamMembersController < ApplicationController ...@@ -40,7 +43,7 @@ class TeamMembersController < ApplicationController
@team_member.destroy @team_member.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to team_project_path(@project) } format.html { redirect_to project_team_index_path(@project) }
format.js { render nothing: true } format.js { render nothing: true }
end end
end end
......
...@@ -16,7 +16,7 @@ class CommitDecorator < ApplicationDecorator ...@@ -16,7 +16,7 @@ class CommitDecorator < ApplicationDecorator
# In case this first line is longer than 80 characters, it is cut off # In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended. # after 70 characters and ellipses (`&hellp;`) are appended.
def title def title
return no_commit_message unless safe_message return no_commit_message if safe_message.blank?
title_end = safe_message.index(/\n/) title_end = safe_message.index(/\n/)
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80) if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
......
...@@ -62,7 +62,7 @@ module ApplicationHelper ...@@ -62,7 +62,7 @@ module ApplicationHelper
{ label: "#{@project.name} / Wall", url: wall_project_path(@project) }, { label: "#{@project.name} / Wall", url: wall_project_path(@project) },
{ label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) }, { label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) },
{ label: "#{@project.name} / Commits", url: project_commits_path(@project) }, { label: "#{@project.name} / Commits", url: project_commits_path(@project) },
{ label: "#{@project.name} / Team", url: team_project_path(@project) } { label: "#{@project.name} / Team", url: project_team_index_path(@project) }
] ]
end end
...@@ -104,7 +104,8 @@ module ApplicationHelper ...@@ -104,7 +104,8 @@ module ApplicationHelper
# Profile Area # Profile Area
when :profile; current_page?(controller: "profile", action: :show) when :profile; current_page?(controller: "profile", action: :show)
when :password; current_page?(controller: "profile", action: :password) when :history; current_page?(controller: "profile", action: :history)
when :account; current_page?(controller: "profile", action: :account)
when :token; current_page?(controller: "profile", action: :token) when :token; current_page?(controller: "profile", action: :token)
when :design; current_page?(controller: "profile", action: :design) when :design; current_page?(controller: "profile", action: :design)
when :ssh_keys; controller.controller_name == "keys" when :ssh_keys; controller.controller_name == "keys"
...@@ -135,4 +136,10 @@ module ApplicationHelper ...@@ -135,4 +136,10 @@ module ApplicationHelper
"Never" "Never"
end end
end end
def authbutton(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag("authbuttons/#{file_name}",
alt: "Sign in with #{provider.to_s.titleize}")
end
end end
...@@ -11,7 +11,9 @@ module GitlabMarkdownHelper ...@@ -11,7 +11,9 @@ module GitlabMarkdownHelper
# explicitly produce the correct linking behavior (i.e. # explicitly produce the correct linking behavior (i.e.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>"). # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
def link_to_gfm(body, url, html_options = {}) def link_to_gfm(body, url, html_options = {})
gfm_body = gfm(body, html_options) return "" if body.blank?
gfm_body = gfm(escape_once(body), html_options)
gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match| gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
"</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1 "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
......
module NotesHelper
def loading_more_notes?
params[:loading_more].present?
end
def loading_new_notes?
params[:loading_new].present?
end
def note_vote_class(note)
if note.upvote?
"vote upvote"
elsif note.downvote?
"vote downvote"
end
end
end
module ProfileHelper
def oauth_active_class provider
if current_user.provider == provider.to_s
'active'
end
end
end
...@@ -2,5 +2,9 @@ module ProjectsHelper ...@@ -2,5 +2,9 @@ module ProjectsHelper
def grouper_project_members(project) def grouper_project_members(project)
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
end end
def remove_from_team_message(project, member)
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
end
end end
...@@ -8,7 +8,7 @@ module TabHelper ...@@ -8,7 +8,7 @@ module TabHelper
end end
def project_tab_class def project_tab_class
[:show, :files, :team, :edit, :update].each do |action| [:show, :files, :edit, :update].each do |action|
return "current" if current_page?(controller: "projects", action: action, id: @project) return "current" if current_page?(controller: "projects", action: action, id: @project)
end end
......
...@@ -18,6 +18,7 @@ module TreeHelper ...@@ -18,6 +18,7 @@ module TreeHelper
end end
def tree_full_path(content) def tree_full_path(content)
content.name.force_encoding('utf-8')
if params[:path] if params[:path]
File.join(params[:path], content.name) File.join(params[:path], content.name)
else else
......
...@@ -42,6 +42,14 @@ class Event < ActiveRecord::Base ...@@ -42,6 +42,14 @@ class Event < ActiveRecord::Base
push? || issue? || merge_request? || membership_changed? push? || issue? || merge_request? || membership_changed?
end end
def project_name
if project
project.name
else
"(deleted)"
end
end
def push? def push?
action == self.class::Pushed && valid_push? action == self.class::Pushed && valid_push?
end end
......
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Upvote include Votes
acts_as_taggable_on :labels acts_as_taggable_on :labels
......
...@@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit") ...@@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Upvote include Votes
BROKEN_DIFF = "--broken-diff" BROKEN_DIFF = "--broken-diff"
......
...@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base ...@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base
scope :today, where("created_at >= :date", date: Date.today) scope :today, where("created_at >= :date", date: Date.today)
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days)) scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
scope :since, lambda { |day| where("created_at >= :date", date: (day)) } scope :since, lambda { |day| where("created_at >= :date", date: (day)) }
scope :fresh, order("created_at DESC") scope :fresh, order("created_at ASC, id ASC")
scope :inc_author_project, includes(:project, :author) scope :inc_author_project, includes(:project, :author)
scope :inc_author, includes(:author) scope :inc_author, includes(:author)
...@@ -105,6 +105,12 @@ class Note < ActiveRecord::Base ...@@ -105,6 +105,12 @@ class Note < ActiveRecord::Base
def upvote? def upvote?
note.start_with?('+1') || note.start_with?(':+1:') note.start_with?('+1') || note.start_with?(':+1:')
end end
# Returns true if this is a downvote note,
# otherwise false is returned
def downvote?
note.start_with?('-1') || note.start_with?(':-1:')
end
end end
# == Schema Information # == Schema Information
# #
......
...@@ -171,6 +171,10 @@ class Project < ActiveRecord::Base ...@@ -171,6 +171,10 @@ class Project < ActiveRecord::Base
end end
end end
def wiki_notes
Note.where(noteable_id: wikis.map(&:id), noteable_type: 'Wiki', project_id: self.id)
end
def project_id def project_id
self.id self.id
end end
......
...@@ -16,7 +16,7 @@ class Tree ...@@ -16,7 +16,7 @@ class Tree
def initialize(raw_tree, project, ref = nil, path = nil) def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path, @project, @ref, @path = project, ref, path,
@tree = if path @tree = if path
raw_tree / path raw_tree / path.dup.force_encoding('ascii-8bit')
else else
raw_tree raw_tree
end end
......
...@@ -86,33 +86,20 @@ class User < ActiveRecord::Base ...@@ -86,33 +86,20 @@ class User < ActiveRecord::Base
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end end
def self.find_for_ldap_auth(auth, signed_in_resource=nil) def self.create_from_omniauth(auth, ldap = false)
uid = auth.info.uid gitlab_auth.create_from_omniauth(auth, ldap)
provider = auth.provider end
name = auth.info.name.force_encoding("utf-8")
email = auth.info.email.downcase unless auth.info.email.nil? def self.find_or_new_for_omniauth(auth)
raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil? gitlab_auth.find_or_new_for_omniauth(auth)
if @user = User.find_by_extern_uid_and_provider(uid, provider)
@user
# workaround for backward compatibility
elsif @user = User.find_by_email(email)
logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"
@user.update_attributes(:extern_uid => uid, :provider => provider)
@user
else
logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}"
password = Devise.friendly_token[0, 8].downcase
@user = User.create(
:extern_uid => uid,
:provider => provider,
:name => name,
:email => email,
:password => password,
:password_confirmation => password,
:projects_limit => Gitlab.config.default_projects_limit
)
end end
def self.find_for_ldap_auth(auth, signed_in_resource = nil)
gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
end
def self.gitlab_auth
Gitlab::Auth.new
end end
def self.search query def self.search query
...@@ -148,4 +135,3 @@ end ...@@ -148,4 +135,3 @@ end
# bio :string(255) # bio :string(255)
# blocked :boolean(1) default(FALSE), not null # blocked :boolean(1) default(FALSE), not null
# #
...@@ -28,7 +28,6 @@ class Wiki < ActiveRecord::Base ...@@ -28,7 +28,6 @@ class Wiki < ActiveRecord::Base
end end
new_wiki new_wiki
end end
end end
end end
# == Schema Information # == Schema Information
......
...@@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer ...@@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer
end end
def after_destroy(project) def after_destroy(project)
log_info("Project \"#{project.name}\" was removed")
project.destroy_repository project.destroy_repository
end end
def after_create project
log_info("#{project.owner.name} created a new project \"#{project.name}\"")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end end
class UserObserver < ActiveRecord::Observer class UserObserver < ActiveRecord::Observer
def after_create(user) def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
Notify.new_user_email(user.id, user.password).deliver Notify.new_user_email(user.id, user.password).deliver
end end
def after_destroy user
log_info("User \"#{user.name}\" (#{user.email}) was removed")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end end
module Upvote
# Return the number of +1 comments (upvotes)
def upvotes
notes.select(&:upvote?).size
end
end
module Votes
# Return the number of +1 comments (upvotes)
def upvotes
notes.select(&:upvote?).size
end
def upvotes_in_percent
if votes_count.zero?
0
else
100.0 / votes_count * upvotes
end
end
# Return the number of -1 comments (downvotes)
def downvotes
notes.select(&:downvote?).size
end
def downvotes_in_percent
if votes_count.zero?
0
else
100.0 - upvotes_in_percent
end
end
# Return the total number of votes
def votes_count
upvotes + downvotes
end
end
.file_holder#README %ul.nav.nav-tabs.log-tabs
%li.active
= link_to "githost.log", "#githost", 'data-toggle' => 'tab'
%li
= link_to "application.log", "#application", 'data-toggle' => 'tab'
.tab-content
.tab-pane.active#githost
.file_holder#README
.file_title .file_title
%i.icon-file %i.icon-file
githost.log githost.log
.file_content.logs .file_content.logs
%ol %ol
- Gitlab::Logger.read_latest.each do |line| - Gitlab::GitLogger.read_latest.each do |line|
%li
%p= line
.tab-pane#application
.file_holder#README
.file_title
%i.icon-file
application.log
.file_content.logs
%ol
- Gitlab::AppLogger.read_latest.each do |line|
%li %li
%p= line %p= line
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
- unless project.new_record? - unless project.new_record?
.clearfix .clearfix
= f.label :owner_id = f.label :owner_id
.input= f.select :owner_id, User.all.map { |user| [user.name, user.id] } .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
- if project.repo_exists? - if project.repo_exists?
.clearfix .clearfix
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
:javascript :javascript
$(function(){ $(function(){
$('#project_owner_id').chosen();
new Projects(); new Projects();
}) })
...@@ -71,25 +71,11 @@ ...@@ -71,25 +71,11 @@
%th Project Access: %th Project Access:
%tr %tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
%tr %tr
%td= submit_tag 'Add', class: "btn primary" %td= submit_tag 'Add', class: "btn primary"
%td %td
Read more about project permissions Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink" %strong= link_to "here", help_permissions_path, class: "vlink"
:css
form select {
width:150px;
}
#user_ids {
width:300px;
}
:javascript
$('select#user_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();
%h3 Resque %h3.page_title Resque
%iframe{src: resque_url, width: 1168, height: 600, style: "border: none"} %br
.ui-box
%iframe{src: resque_url, width: '100%', height: 600, style: "border: none"}
...@@ -8,20 +8,9 @@ ...@@ -8,20 +8,9 @@
.clearfix .clearfix
%label Project Access: %label Project Access:
.input .input
= f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select" = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
%br %br
.actions .actions
= f.submit 'Save', class: "btn primary" = f.submit 'Save', class: "btn primary"
= link_to 'Cancel', :back, class: "btn" = link_to 'Cancel', :back, class: "btn"
:css
form select {
width:300px;
}
:javascript
$('select#team_member_user_id').chosen();
$('select#team_member_project_id').chosen();
$('select#team_member_repo_access').chosen();
$('select#team_member_project_access').chosen();
...@@ -68,8 +68,8 @@ ...@@ -68,8 +68,8 @@
%th Project Access: %th Project Access:
%tr %tr
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
%tr %tr
%td= submit_tag 'Add', class: "btn primary" %td= submit_tag 'Add', class: "btn primary"
...@@ -97,17 +97,3 @@ ...@@ -97,17 +97,3 @@
%td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
:css
form select {
width:150px;
}
#project_ids {
width:300px;
}
:javascript
$('select#project_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
%strong Browse Code » %strong Browse Code »
%h3.commit-title.page_title %h3.commit-title.page_title
= gfm @commit.title = gfm escape_once(@commit.title)
- if @commit.description.present? - if @commit.description.present?
%pre.commit-description %pre.commit-description
= gfm @commit.description = gfm escape_once(@commit.description)
.commit-info .commit-info
.row .row
.span4 .span4
......
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select"
= hidden_field_tag :destination, "commits"
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"} %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
= link_to project_commits_path(@project) do = link_to project_commits_path(@project) do
Commits Commits
...@@ -20,14 +16,8 @@ ...@@ -20,14 +16,8 @@
Tags Tags
%span.badge= @project.repo.tag_count %span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token - if current_page?(project_commits_path(@project)) && current_user.private_token
%li.right %li.right
%span.rss-icon %span.rss-icon
= link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do
= image_tag "rss_ui.png", title: "feed" = image_tag "rss_ui.png", title: "feed"
:javascript
$(function(){
$('.project-refs-select').chosen();
});
...@@ -13,14 +13,11 @@ ...@@ -13,14 +13,11 @@
%td.old_line %td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- if @comments_allowed - if @comments_allowed
= link_to "", "#", class: "line_note_link", "line_code" => line_code, title: "Add note for this line" = render "notes/per_line_note_link", line_code: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;" %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
- if @comments_allowed - if @comments_allowed
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at).reverse - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
- unless comments.empty? - unless comments.empty?
- comments.each_with_index do |note, i| = render "notes/per_line_notes_with_reply", notes: comments
= render "notes/reply_button", line_code: line_code if i.zero?
= render "notes/per_line_show", note: note
- @line_notes.reject!{ |n| n == note }
= render "commits/commit_box" = render "commits/commit_box"
= render "commits/diffs", diffs: @commit.diffs = render "commits/diffs", diffs: @commit.diffs
= render "notes/notes", tid: @commit.id, tt: "commit" = render "notes/notes_with_form", tid: @commit.id, tt: "commit"
= render "notes/per_line_form" = render "notes/per_line_form"
......
...@@ -31,13 +31,19 @@ ...@@ -31,13 +31,19 @@
%span= project_last_activity(project) %span= project_last_activity(project)
.bottom= paginate @projects, theme: "gitlab" .bottom= paginate @projects, theme: "gitlab"
%hr
%div %div
%span.rss-icon %span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
= image_tag "rss_ui.png", title: "feed" = image_tag "rss_ui.png", title: "feed"
%strong News Feed %strong News Feed
%hr
.gitlab-promo
= link_to "Homepage", "http://gitlabhq.com"
= link_to "Blog", "http://blog.gitlabhq.com"
= link_to "@gitlabhq", "https://twitter.com/gitlabhq"
- else - else
%h3.nothing_here_message There are no projects you have access to. %h3.nothing_here_message There are no projects you have access to.
%br %br
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
$(function() { $(function() {
$('#new_user').toggle(); $('#new_user').toggle();
}); });
= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
= f.text_field :email, :class => "text top", :placeholder => "Email" = f.text_field :email, :class => "text top", :placeholder => "Email"
= f.password_field :password, :class => "text bottom", :placeholder => "Password" = f.password_field :password, :class => "text bottom", :placeholder => "Password"
- if devise_mapping.rememberable? - if devise_mapping.rememberable?
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.right .right
= render :partial => "devise/shared/links" = render :partial => "devise/shared/links"
- if devise_mapping.omniauthable? - if devise_mapping.omniauthable?
- resource_class.omniauth_providers.each do |provider|
%hr/ %hr/
= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" - resource_class.omniauth_providers.each do |provider|
%br/ %span
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
%strong.cdark= commit.author_name %strong.cdark= commit.author_name
&ndash; &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm truncate(commit.title, length: 50) rescue "--broken encoding" = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%strong #{event.author_name} %strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name %span.event_label{class: event.action_name}= event.action_name
project project
%strong= link_to event.project.name, event.project %strong= link_to event.project_name, event.project
%span.cgray %span.cgray
= time_ago_in_words(event.created_at) = time_ago_in_words(event.created_at)
ago. ago.
......
...@@ -18,12 +18,12 @@ ...@@ -18,12 +18,12 @@
= f.label :assignee_id do = f.label :assignee_id do
%i.icon-user %i.icon-user
Assign to Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }) .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
.issue_milestone .issue_milestone
= f.label :milestone_id do = f.label :milestone_id do
%i.icon-time %i.icon-time
Milestone Milestone
.input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }) .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
.issue_description .issue_description
.clearfix .clearfix
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
.right .right
- issue.labels.each do |label| - issue.labels.each do |label|
%span.label.label-issue.grouped %span.label.label-tag.grouped
%i.icon-tag %i.icon-tag
= label.name = label.name
- if issue.notes.any? - if issue.notes.any?
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
- else - else
&nbsp; &nbsp;
- if issue.upvotes > 0 - if issue.votes_count > 0
%span.badge.badge-success= "+#{issue.upvotes}" = render 'votes/votes_inline', votable: issue
= render "form" = render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
});
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
= hidden_field_tag :project_id, @project.id, { id: 'project_id' } = hidden_field_tag :project_id, @project.id, { id: 'project_id' }
= hidden_field_tag :status, params[:f] = hidden_field_tag :status, params[:f]
= search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib' } = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib search-text-input' }
.clearfix .clearfix
......
= render "form" = render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
});
...@@ -8,22 +8,22 @@ ...@@ -8,22 +8,22 @@
%span.right %span.right
- if can?(current_user, :admin_project, @project) || @issue.author == current_user - if can?(current_user, :admin_project, @project) || @issue.author == current_user
- if @issue.closed - if @issue.closed
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small" = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped success"
- else - else
= link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small", title: "Close Issue" = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close Issue"
- if can?(current_user, :admin_project, @project) || @issue.author == current_user - if can?(current_user, :admin_project, @project) || @issue.author == current_user
= link_to edit_project_issue_path(@project, @issue), class: "btn small" do = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
%i.icon-edit %i.icon-edit
Edit Edit
%br .right
- if @issue.upvotes > 0 .span3#votes= render 'votes/votes_block', votable: @issue
.upvotes#upvotes= "+#{pluralize @issue.upvotes, 'upvote'}"
.back_link .back_link
= link_to project_issues_path(@project) do = link_to project_issues_path(@project) do
&larr; To issues list &larr; To issues list
.main_box .main_box
.top_box_content .top_box_content
%h4 %h4
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
.alert-message.error.status_info Closed .alert-message.error.status_info Closed
- else - else
.alert-message.success.status_info Open .alert-message.success.status_info Open
= gfm @issue.title = gfm escape_once(@issue.title)
.middle_box_content .middle_box_content
%cite.cgray Created by %cite.cgray Created by
...@@ -61,4 +61,4 @@ ...@@ -61,4 +61,4 @@
= markdown @issue.description = markdown @issue.description
.issue_notes#notes= render "notes/notes", tid: @issue.id, tt: "issue" .issue_notes.voting_notes#notes= render "notes/notes_with_form", tid: @issue.id, tt: "issue"
%li.wll %li.wll
%strong= label.name %strong
%i.icon-tag
= label.name
.right .right
%span= pluralize label.count, 'issue' = link_to project_issues_path(label_name: label.name) do
%strong
= pluralize(label.count, 'issue')
= "»"
...@@ -9,20 +9,20 @@ ...@@ -9,20 +9,20 @@
%li.home{class: tab_class(:profile)} %li.home{class: tab_class(:profile)}
= link_to "Profile", profile_path = link_to "Profile", profile_path
%li{class: tab_class(:password)} %li{class: tab_class(:account)}
= link_to "Password", profile_password_path = link_to "Account", profile_account_path
%li{class: tab_class(:ssh_keys)} %li{class: tab_class(:ssh_keys)}
= link_to keys_path do = link_to keys_path do
SSH Keys SSH Keys
%span.count= current_user.keys.count %span.count= current_user.keys.count
%li{class: tab_class(:token)}
= link_to "Token", profile_token_path
%li{class: tab_class(:design)} %li{class: tab_class(:design)}
= link_to "Design", profile_design_path = link_to "Design", profile_design_path
%li{class: tab_class(:history)}
= link_to "History", profile_history_path
.content .content
= yield = yield
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.padded .padded
= f.label :source_branch, "From", class: "control-label" = f.label :source_branch, "From", class: "control-label"
.controls .controls
= f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") = f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'})
.mr_source_commit .mr_source_commit
.span2 .span2
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.padded .padded
= f.label :target_branch, "To", class: "control-label" = f.label :target_branch, "To", class: "control-label"
.controls .controls
= f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") = f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'})
.mr_target_commit .mr_target_commit
%h4.cdark 2. Fill info %h4.cdark 2. Fill info
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
= f.label :assignee_id do = f.label :assignee_id do
%i.icon-user %i.icon-user
Assign to Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, style: "width:250px") .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
.control-group .control-group
...@@ -56,18 +56,12 @@ ...@@ -56,18 +56,12 @@
= link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do
Cancel Cancel
:javascript :javascript
$(function(){ $(function(){
disableButtonIfEmptyField("#merge_request_title", ".save-btn"); disableButtonIfEmptyField("#merge_request_title", ".save-btn");
$('select#merge_request_assignee_id').chosen();
$('select#merge_request_source_branch').chosen();
$('select#merge_request_target_branch').chosen();
var source_branch = $("#merge_request_source_branch"); var source_branch = $("#merge_request_source_branch");
var target_branch = $("#merge_request_target_branch"); var target_branch = $("#merge_request_target_branch");
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() });
...@@ -79,4 +73,3 @@ ...@@ -79,4 +73,3 @@
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() });
}); });
}); });
...@@ -23,5 +23,6 @@ ...@@ -23,5 +23,6 @@
authored by #{merge_request.author_name} authored by #{merge_request.author_name}
= time_ago_in_words(merge_request.created_at) = time_ago_in_words(merge_request.created_at)
ago ago
- if merge_request.upvotes > 0
%span.badge.badge-success= "+#{merge_request.upvotes}" - if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
%i.icon-list-alt %i.icon-list-alt
Diff Diff
.merge_request_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } .merge_request_notes.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render("notes/notes", tid: @merge_request.id, tt: "merge_request") = render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")
.merge-request-diffs .merge-request-diffs
= render "merge_requests/show/diffs" if @diffs = render "merge_requests/show/diffs" if @diffs
.status .status
......
= render "show" = render "show"
:javascript
$(function(){
PerLineNotes.init();
});
:plain :plain
$(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}"); $(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}");
$(function(){
PerLineNotes.init();
});
:plain :plain
$(".merge-request-notes").html("#{escape_javascript(render("notes/notes", tid: @merge_request.id, tt: "merge_request"))}"); $(".merge-request-notes").html("#{escape_javascript(render notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")}");
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
.alert-message.error.status_info Closed .alert-message.error.status_info Closed
- else - else
.alert-message.success.status_info Open .alert-message.success.status_info Open
= gfm @merge_request.title = gfm escape_once(@merge_request.title)
.middle_box_content .middle_box_content
%div %div
......
...@@ -23,10 +23,8 @@ ...@@ -23,10 +23,8 @@
%i.icon-edit %i.icon-edit
Edit Edit
%br .right
- if @merge_request.upvotes > 0 .span3#votes= render 'votes/votes_block', votable: @merge_request
.upvotes#upvotes= "+#{pluralize @merge_request.upvotes, 'upvote'}"
.back_link .back_link
= link_to project_merge_requests_path(@project) do = link_to project_merge_requests_path(@project) do
......
= render "form" = render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
});
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
.alert-message.error.status_info Closed .alert-message.error.status_info Closed
- else - else
.alert-message.success.status_info Open .alert-message.success.status_info Open
= gfm @milestone.title = gfm escape_once(@milestone.title)
%small.right= @milestone.expires_at %small.right= @milestone.expires_at
.middle_box_content .middle_box_content
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.clearfix .clearfix
.row.note_advanced_opts.hide .row.note_advanced_opts
.span3 .span3
= f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note" = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
= link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link' = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
$('.note-form-holder #preview-link').text('Preview'); $('.note-form-holder #preview-link').text('Preview');
$('.note-form-holder #preview-note').hide(); $('.note-form-holder #preview-note').hide();
$('.note-form-holder').show(); $('.note-form-holder').show();
NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}"); NoteList.appendNewNote(#{note.id}, "#{escape_javascript(render "notes/note", note: note)}");
- else - else
:plain :plain
$(".note-form-holder").replaceWith("#{escape_javascript(render('form'))}"); $(".note-form-holder").replaceWith("#{escape_javascript(render 'form')}");
- if note.valid?
:plain
$(".per_line_form").hide();
$('.line-note-form-holder textarea').val("");
$("a.line_note_reply_link[line_code='#{note.line_code}']").closest("tr").remove();
var trEl = $(".#{note.line_code}").parent();
trEl.after("#{escape_javascript(render partial: "notes/per_line_show", locals: {note: note})}");
trEl.after("#{escape_javascript(render partial: "notes/reply_button", locals: {line_code: note.line_code})}");
- if note.valid?
:plain
// hide and reset the form
$(".per_line_form").hide();
$('.line-note-form-holder textarea').val("");
// find the reply button for this line
// (might not be there if this is the first note)
var trRpl = $("a.line_note_reply_link[data-line-code='#{note.line_code}']").closest("tr");
if (trRpl.size() == 0) {
// find the commented line ...
var trEl = $(".#{note.line_code}").parent();
// ... and insert the note and the reply button after it
trEl.after("#{escape_javascript(render "notes/per_line_reply_button", line_code: note.line_code)}");
trEl.after("#{escape_javascript(render "notes/per_line_note", note: note)}");
} else {
// instert new note before reply button
trRpl.before("#{escape_javascript(render "notes/per_line_note", note: note)}");
}
- unless @notes.blank?
- if params[:last_id]
:plain
NoteList.replace("#{escape_javascript(render(partial: 'notes/notes_list'))}");
- elsif params[:first_id]
:plain
NoteList.append(#{@notes.last.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
- else
:plain
NoteList.setContent(#{@notes.last.id}, #{@notes.first.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
- else
- if params[:first_id]
:plain
NoteList.append(#{params[:first_id]}, "");
%li{id: dom_id(note), class: "note"} %li{id: dom_id(note), class: "note #{note_vote_class(note)}"}
= image_tag gravatar_icon(note.author.email), class: "avatar s32" = image_tag gravatar_icon(note.author.email), class: "avatar s32"
%div.note-author %div.note-author
%strong= note.author_name %strong= note.author_name
...@@ -6,6 +6,14 @@ ...@@ -6,6 +6,14 @@
%cite.cgray %cite.cgray
= time_ago_in_words(note.updated_at) = time_ago_in_words(note.updated_at)
ago ago
- if note.upvote?
%span.label.label-success
%i.icon-thumbs-up
\+1
- if note.downvote?
%span.label.label-error
%i.icon-thumbs-down
\-1
- if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
= link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do
%i.icon-trash %i.icon-trash
......
- if can? current_user, :write_note, @project - @notes.each do |note|
= render "notes/form" - next unless note.author
.clear = render "note", note: note
%hr
%ul#new_notes_list
%ul#notes-list
.status
:javascript
$(function(){
NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
});
- @notes.each do |note|
- next unless note.author
= render partial: "notes/show", locals: {note: note}
%ul#notes-list
%ul#new-notes-list
.notes-status
- if can? current_user, :write_note, @project
= render "notes/common_form"
:javascript
$(function(){
NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
});
%tr.line_notes_row %tr.line_notes_row
%td{colspan: 3} %td{colspan: 3}
%ul %ul
= render partial: "notes/show", locals: {note: note} = render "notes/note", note: note
= link_to "", "#", class: "line_note_link", data: { line_code: line_code }, title: "Add note for this line"
- notes.each do |note|
= render "notes/per_line_note", note: note
= render "notes/per_line_reply_button", line_code: notes.first.line_code
%tr.line_notes_row.reply %tr.line_notes_row.reply
%td{colspan: 3} %td{colspan: 3}
%i.icon-comment %i.icon-comment
= link_to "Reply", "#", class: "line_note_reply_link", "line_code" => line_code, title: "Add note for this line" = link_to "Reply", "#", class: "line_note_reply_link", data: { line_code: line_code }, title: "Add note for this line"
- if can? current_user, :write_note, @project
= render "notes/common_form"
%ul.reversed#new-notes-list
%ul.reversed#notes-list
.notes-status
:javascript
$(function(){
NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
});
- if @note.line_code - if @note.line_code
= render "create_line", note: @note = render "create_per_line_note", note: @note
- else - else
= render "create_common", note: @note = render "create_common_note", note: @note
-# Enable submit button -# Enable submit button
:plain :plain
......
= render "notes/load" - unless @notes.blank?
- if loading_more_notes?
:plain
NoteList.appendMoreNotes(#{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}");
- elsif loading_new_notes?
:plain
NoteList.replaceNewNotes("#{escape_javascript(render 'notes/notes')}");
- else
:plain
NoteList.setContent(#{@notes.first.id}, #{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}");
- else
- if loading_more_notes?
:plain
NoteList.finishedLoadingMore();
- if Gitlab.config.omniauth_enabled?
%fieldset
%legend
%h3.page_title Social Accounts
.oauth_select_holder
%p.hint Tip: Click on icon to activate sigin with one of the following services
- User.omniauth_providers.each do |provider|
%span{class: oauth_active_class(provider) }
= link_to authbutton(provider, 32), omniauth_authorize_path(User, provider)
%fieldset
%legend
%h3.page_title
Private token
%span.cred.right
keep it in secret!
.padded
= form_for @user, url: profile_reset_private_token_path, method: :put do |f|
.data
%p.slead
Private token used to access application resources without authentication.
%br
It can be used for atom feed or API
%p.cgray
- if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "xxlarge large_text"
= f.submit 'Reset', confirm: "Are you sure?", class: "btn primary btn-build-token"
- else
%span You don`t have one yet. Click generate to fix it.
= f.submit 'Generate', class: "btn success btn-build-token"
%fieldset
%legend
%h3.page_title Password
= form_for @user, url: profile_password_path, method: :put do |f|
.padded
%p.slead After successful password update you will be redirected to login page where you should login with new password
-if @user.errors.any?
.alert-message.block-message.error
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.clearfix
= f.label :password
.input= f.password_field :password
.clearfix
= f.label :password_confirmation
.input= f.password_field :password_confirmation
.actions
= f.submit 'Save', class: "btn save-btn"
.profile_history
= render @events
%hr
= paginate @events, theme: "gitlab"
%h3.page_title Password
%hr
= form_for @user, url: profile_password_path, method: :put do |f|
.data
%p.slead After successful password update you will be redirected to login page where you should login with new password
-if @user.errors.any?
.alert-message.block-message.error
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
.clearfix
= f.label :password
.input= f.password_field :password
.clearfix
= f.label :password_confirmation
.input= f.password_field :password_confirmation
.actions
= f.submit 'Save', class: "btn save-btn"
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
%small %small
= @user.email = @user.email
%hr %hr
= form_for @user, url: profile_update_path, method: :put, html: { class: "edit_user form-horizontal" } do |f| = form_for @user, url: profile_update_path, method: :put, html: { class: "edit_user form-horizontal" } do |f|
...@@ -28,7 +27,23 @@ ...@@ -28,7 +27,23 @@
= f.text_field :email, class: "input-xlarge" = f.text_field :email, class: "input-xlarge"
%span.help-block We also use email for avatar detection. %span.help-block We also use email for avatar detection.
.span5.right
%div.tips
%h6 Tips:
%ul
-unless Gitlab.config.disable_gravatar?
%li
%p.hint You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"}
- if Gitlab.config.omniauth_enabled? && @user.provider?
%li
%p.hint
You can login through #{@user.provider.titleize}!
= link_to "click here to change", profile_account_path
%hr %hr
.row
.span7
.control-group .control-group
= f.label :skype, class: "control-label" = f.label :skype, class: "control-label"
.controls= f.text_field :skype, class: "input-xlarge" .controls= f.text_field :skype, class: "input-xlarge"
...@@ -44,12 +59,8 @@ ...@@ -44,12 +59,8 @@
= f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250 = f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250
%span.help-block Tell us about yourself in fewer than 250 characters. %span.help-block Tell us about yourself in fewer than 250 characters.
.span5.right .span5.right
.ui-box.white
-unless Gitlab.config.disable_gravatar? .ui-box-body
%p.alert.alert-info
%strong Tip:
You can change your avatar at gravatar.com
%h4 %h4
Personal projects: Personal projects:
%small.right %small.right
...@@ -59,12 +70,13 @@ ...@@ -59,12 +70,13 @@
.progress .progress
.bar{style: "width: #{current_user.projects_limit_percent}%;"} .bar{style: "width: #{current_user.projects_limit_percent}%;"}
.ui-box.white
.ui-box-body
%h4 %h4
SSH public keys: SSH public keys:
%small.right %strong.right= link_to current_user.keys.count, keys_path
%span= link_to current_user.keys.count, keys_path
= link_to "Add Public Key", new_key_path, class: "btn small right" = link_to "Add Public Key", new_key_path, class: "btn small"
.form-actions .form-actions
= f.submit 'Save', class: "btn save-btn" = f.submit 'Save', class: "btn save-btn"
%h3.page_title
Private token
%span.cred.right
keep it in secret!
%hr
= form_for @user, url: profile_reset_private_token_path, method: :put do |f|
.data
%p.slead
Private token used to access application resources without authentication.
%br
It can be used for atom feed or API
%p.cgray
- if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "xxlarge large_text"
- else
You don`t have one yet. Click generate to fix it.
.actions
- if current_user.private_token
= f.submit 'Reset', confirm: "Are you sure?", class: "btn"
- else
= f.submit 'Generate', class: "btn primary"
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
= link_to project_path(@project), class: "activities-tab tab" do = link_to project_path(@project), class: "activities-tab tab" do
%i.icon-home %i.icon-home
Show Show
%li{ class: " #{'active' if (controller.controller_name == "team_members") || current_page?(team_project_path(@project)) }" } %li{ class: " #{'active' if (controller.controller_name == "team_members") || current_page?(project_team_index_path(@project)) }" }
= link_to team_project_path(@project), class: "team-tab tab" do = link_to project_team_index_path(@project), class: "team-tab tab" do
%i.icon-user %i.icon-user
Team Team
%li{ class: "#{'active' if current_page?(files_project_path(@project)) }" } %li{ class: "#{'active' if current_page?(files_project_path(@project)) }" }
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
%h3.page_title Edit Project %h3.page_title Edit Project
%hr %hr
= render "projects/form" = render "projects/form"
%div.ajax_loader.hide %div.save-project-loader.hide
%center %center
%div.padded= image_tag "ajax_loader.gif" = image_tag "ajax_loader.gif"
%h3.prepend-top Saving project &amp; repository. Please wait... %h3 Saving project. Please wait a few minutes
:javascript :javascript
$(function(){ new Projects(); }); $(function(){ new Projects(); });
%div.wall_page %div.wall_page
= render "notes/notes", tid: nil, tt: "wall" = render "notes/reversed_notes_with_form", tid: nil, tt: "wall"
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
.entry.clearfix .entry.clearfix
= f.label :name, "Branch" = f.label :name, "Branch"
.span3 .span3
= f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , { include_blank: "-- Select branch" }, { class: "span3" }) = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "chosen span3"})
&nbsp; &nbsp;
= f.submit 'Protect', class: "primary btn" = f.submit 'Protect', class: "primary btn"
...@@ -46,6 +46,3 @@ ...@@ -46,6 +46,3 @@
%td %td
- if can? current_user, :admin_project, @project - if can? current_user, :admin_project, @project
= link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small" = link_to 'Unprotect', [@project, branch], confirm: 'Are you sure?', method: :delete, class: "danger btn small"
:javascript
$('select#protected_branch_name').chosen();
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li %li
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form", remote: true do = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]}
= select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select"
= hidden_field_tag :destination, "tree"
= hidden_field_tag :path, params[:path]
%li{class: "#{'active' if (controller.controller_name == "refs") }"} %li{class: "#{'active' if (controller.controller_name == "refs") }"}
= link_to tree_project_ref_path(@project, @ref) do = link_to tree_project_ref_path(@project, @ref) do
Source Source
......
...@@ -47,18 +47,16 @@ ...@@ -47,18 +47,16 @@
:javascript :javascript
$(function(){ $(function(){
$('.project-refs-select').chosen();
history.pushState({ path: this.path }, '', "#{@history_path}"); history.pushState({ path: this.path }, '', "#{@history_path}");
}); });
- unless tree.is_blob?
:javascript
// Load last commit log for each file in tree // Load last commit log for each file in tree
$(window).load(function(){ $(window).load(function(){
ajaxGet('#{@logs_path}'); ajaxGet('#{@logs_path}');
}); });
- if params[:path] && request.xhr? - if params[:path] && request.xhr?
:javascript :javascript
$(window).unbind('popstate'); $(window).unbind('popstate');
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.file_title .file_title
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= name = name.force_encoding('utf-8')
%small #{file.mode} %small #{file.mode}
%span.options %span.options
= link_to "raw", blob_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small", target: "_blank" = link_to "raw", blob_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small", target: "_blank"
......
...@@ -38,8 +38,3 @@ ...@@ -38,8 +38,3 @@
= preserve do = preserve do
%pre %pre
= Gitlab::Encode.utf8 lines.join("\n") = Gitlab::Encode.utf8 lines.join("\n")
:javascript
$(function(){
$('.project-refs-select').chosen();
});
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
%code= commit.short_id %code= commit.short_id
= image_tag gravatar_icon(commit.author_email), class: "", width: 16 = image_tag gravatar_icon(commit.author_email), class: "", width: 16
= gfm truncate(commit.title, length: 40) = gfm escape_once(truncate(commit.title, length: 40))
%span.update-author.right %span.update-author.right
= time_ago_in_words(commit.committed_date) = time_ago_in_words(commit.committed_date)
ago ago
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= link_to project_commits_path(@project, commit.id) do = link_to project_commits_path(@project, commit.id) do
%code= commit.short_id %code= commit.short_id
= image_tag gravatar_icon(commit.author_email), class: "", width: 16 = image_tag gravatar_icon(commit.author_email), class: "", width: 16
= gfm truncate(commit.title, length: 40) = gfm escape_once(truncate(commit.title, length: 40))
%td %td
%span.right.cgray %span.right.cgray
= time_ago_in_words(commit.committed_date) = time_ago_in_words(commit.committed_date)
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
= link_to project_commit_path(@project, commit.id) do = link_to project_commit_path(@project, commit.id) do
%code= commit.short_id %code= commit.short_id
= image_tag gravatar_icon(commit.author_email), class: "", width: 16 = image_tag gravatar_icon(commit.author_email), class: "", width: 16
= gfm truncate(commit.title, length: 40) = gfm escape_once(truncate(commit.title, length: 40))
%td %td
%span.update-author.right %span.update-author.right
= time_ago_in_words(commit.committed_date) = time_ago_in_words(commit.committed_date)
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
= label_tag :search do = label_tag :search do
%strong Looking for %strong Looking for
.input .input
= text_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge", id: "dashboard_search" = search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
= submit_tag 'Search', class: "btn primary" = submit_tag 'Search', class: "btn primary wide"
- if params[:search].present? - if params[:search].present?
%br %br
%h3 %h3
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
.row .row
.span6 .span6
%table %table
%thead
%tr %tr
%th Projects %th Projects
%tbody %tbody
...@@ -32,6 +33,7 @@ ...@@ -32,6 +33,7 @@
%h4.nothing_here_message No Projects %h4.nothing_here_message No Projects
%br %br
%table %table
%thead
%tr %tr
%th Merge Requests %th Merge Requests
%tbody %tbody
...@@ -50,6 +52,7 @@ ...@@ -50,6 +52,7 @@
%h4.nothing_here_message No Merge Requests %h4.nothing_here_message No Merge Requests
.span6 .span6
%table %table
%thead
%tr %tr
%th Issues %th Issues
%tbody %tbody
......
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do = form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= select_tag "ref", grouped_options_refs, onchange: "this.form.submit();", class: "project-refs-select" = select_tag "ref", grouped_options_refs, class: "project-refs-select chosen"
= hidden_field_tag :destination, destination = hidden_field_tag :destination, destination
- if respond_to?(:path)
:javascript = hidden_field_tag :path, path
$(function(){
$('.project-refs-select').chosen();
})
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.input= f.text_field :file_name, placeholder: "example.rb" .input= f.text_field :file_name, placeholder: "example.rb"
.clearfix .clearfix
= f.label "Lifetime" = f.label "Lifetime"
.input= f.select :expires_at, lifetime_select_options, {}, style: "width:200px;" .input= f.select :expires_at, lifetime_select_options, {}, {class: 'chosen span2'}
.clearfix .clearfix
= f.label :content, "Code" = f.label :content, "Code"
.input= f.text_area :content, class: "span8" .input= f.text_area :content, class: "span8"
...@@ -26,11 +26,3 @@ ...@@ -26,11 +26,3 @@
= link_to "Cancel", project_snippets_path(@project), class: " btn" = link_to "Cancel", project_snippets_path(@project), class: " btn"
- unless @snippet.new_record? - unless @snippet.new_record?
.right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" .right= link_to 'Destroy', [@project, @snippet], confirm: 'Are you sure?', method: :delete, class: "btn right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}"
:javascript
$(function(){
$('select#snippet_expires_at').chosen();
});
...@@ -17,4 +17,4 @@ ...@@ -17,4 +17,4 @@
%div{class: current_user.dark_scheme ? "black" : ""} %div{class: current_user.dark_scheme ? "black" : ""}
= raw @snippet.colorize(options: { linenos: 'True'}) = raw @snippet.colorize(options: { linenos: 'True'})
= render "notes/notes", tid: @snippet.id, tt: "snippet" = render "notes/notes_with_form", tid: @snippet.id, tt: "snippet"
...@@ -10,21 +10,14 @@ ...@@ -10,21 +10,14 @@
%h6 1. Choose people you want in the team %h6 1. Choose people you want in the team
.clearfix .clearfix
= f.label :user_ids, "Peolpe" = f.label :user_ids, "People"
.input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), { class: "xxlarge", multiple: true }) .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true})
%h6 2. Set access level for them %h6 2. Set access level for them
.clearfix .clearfix
= f.label :project_access, "Project Access" = f.label :project_access, "Project Access"
.input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select" .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen"
.actions .actions
= f.submit 'Save', class: "btn save-btn" = f.submit 'Save', class: "btn save-btn"
= link_to "Cancel", team_project_path(@project), class: "btn cancel-btn" = link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn"
:javascript
$('select#user_ids').chosen();
$('select#project_access').chosen();
- user = member.user - user = member.user
- allow_admin = can? current_user, :admin_project, @project - allow_admin = can? current_user, :admin_project, @project
%tr{id: dom_id(member), class: "team_member_row user_#{user.id}"} %tr{id: dom_id(member), class: "team_member_row user_#{user.id}"}
%td %td.span6
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
= image_tag gravatar_icon(user.email, 40), class: "avatar s32" = image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do = link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
%strong= truncate(user.name, lenght: 40) %strong= truncate(user.name, lenght: 40)
%br %br
%div.cgray= user.email %small.cgray= user.email
%td %td.span5
.right .right
- if current_user == user
%span.btn.disabled This is you!
- if @project.owner == user - if @project.owner == user
%span.btn.disabled.success Project Owner %span.btn.disabled.success Owner
- if user.blocked - elsif user.blocked
%span.btn.disabled.blocked Blocked %span.btn.disabled.blocked Blocked
- elsif allow_admin
= link_to project_team_member_path(project_id: @project, id: member.id), confirm: remove_from_team_message(@project, member), method: :delete, class: "very_small btn danger" do
%i.icon-minus.icon-white
- if allow_admin - if allow_admin
= form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f| = form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f|
= f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select" = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2"
= render "project_head" = render "projects/project_head"
%h3.page_title %h3.page_title
Team Members Team Members
%small (#{@project.users_projects.count}) %small (#{@project.users_projects.count})
...@@ -10,6 +10,4 @@ ...@@ -10,6 +10,4 @@
Read more about project permissions Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink" %strong= link_to "here", help_permissions_path, class: "vlink"
= render partial: "team_members/team", locals: {project: @project}
= render partial: "team", locals: {project: @project}
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%hr %hr
.back_link .back_link
%br %br
= link_to team_project_path(@project), class: "" do = link_to project_team_index_path(@project), class: "" do
&larr; To team list &larr; To team list
%br %br
.row .row
......
.votes.votes-block
.progress
.bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
.bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
.upvotes= "#{votable.upvotes} up"
.downvotes= "#{votable.downvotes} down"
.votes.votes-inline
.upvotes= votable.upvotes
.progress
.bar.bar-success{style: "width: #{votable.upvotes_in_percent}%;"}
.bar.bar-danger{style: "width: #{votable.downvotes_in_percent}%;"}
.downvotes= votable.downvotes
...@@ -21,4 +21,4 @@ ...@@ -21,4 +21,4 @@
Delete this page Delete this page
%hr %hr
.wiki_notes#notes= render "notes/notes", tid: @wiki.id, tt: "wiki" .wiki_notes#notes= render "notes/notes_with_form", tid: @wiki.id, tt: "wiki"
<%
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
%>
default: <%= std_opts %> features
wip: --tags @wip:3 --wip features
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
...@@ -25,8 +25,43 @@ app: ...@@ -25,8 +25,43 @@ app:
# backup_keep_time: 604800 # default: 0 (forever) (in seconds) # backup_keep_time: 604800 # default: 0 (forever) (in seconds)
# disable_gravatar: true # default: false - Disable user avatars from Gravatar.com # disable_gravatar: true # default: false - Disable user avatars from Gravatar.com
#
# 2. Auth settings
# ==========================
ldap:
enabled: false
host: '_your_ldap_server'
base: '_the_base_where_you_search_for_users'
port: 636
uid: 'sAMAccountName'
method: 'ssl' # plain
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
omniauth:
# Enable ability for users
# to login via twitter, google ..
enabled: false
# IMPORTANT!
# It allows user to login without having user account
allow_single_sign_on: false
block_auto_created_users: true
# Auth providers
providers:
# - { name: 'google_oauth2', app_id: 'YOUR APP ID',
# app_secret: 'YOUR APP SECRET',
# args: { access_type: 'offline', approval_prompt: '' } }
# - { name: 'twitter', app_id: 'YOUR APP ID',
# app_secret: 'YOUR APP SECRET'}
# - { name: 'github', app_id: 'YOUR APP ID',
# app_secret: 'YOUR APP SECRET' }
# #
# 2. Advanced settings: # 3. Advanced settings:
# ========================== # ==========================
# Git Hosting configuration # Git Hosting configuration
......
...@@ -120,6 +120,22 @@ class Settings < Settingslogic ...@@ -120,6 +120,22 @@ class Settings < Settingslogic
app['backup_keep_time'] || 0 app['backup_keep_time'] || 0
end end
def ldap_enabled?
ldap && ldap['enabled']
rescue Settingslogic::MissingSetting
false
end
def omniauth_enabled?
omniauth && omniauth['enabled']
rescue Settingslogic::MissingSetting
false
end
def omniauth_providers
(omniauth_enabled? && omniauth['providers']) || []
end
def disable_gravatar? def disable_gravatar?
app['disable_gravatar'] || false app['disable_gravatar'] || false
end end
......
...@@ -204,4 +204,21 @@ Devise.setup do |config| ...@@ -204,4 +204,21 @@ Devise.setup do |config|
# manager.intercept_401 = false # manager.intercept_401 = false
# manager.default_strategies(:scope => :user).unshift :some_external_strategy # manager.default_strategies(:scope => :user).unshift :some_external_strategy
# end # end
gl = Gitlab.config
if gl.ldap_enabled?
config.omniauth :ldap,
:host => gl.ldap['host'],
:base => gl.ldap['base'],
:uid => gl.ldap['uid'],
:port => gl.ldap['port'],
:method => gl.ldap['method'],
:bind_dn => gl.ldap['bind_dn'],
:password => gl.ldap['password']
end
gl.omniauth_providers.each do |gl_provider|
config.omniauth gl_provider['name'].to_sym, gl_provider['app_id'], gl_provider['app_secret']
end
end end
# Copy this file to 'omniauth.rb' and configure it as necessary.
# The wiki has further details on configuring each provider.
Devise.setup do |config|
# config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
# config.omniauth :ldap,
# :host => 'YOUR_LDAP_SERVER',
# :base => 'THE_BASE_WHERE_YOU_SEARCH_FOR_USERS',
# :uid => 'sAMAccountName',
# :port => 389,
# :method => :plain,
# :bind_dn => 'THE_FULL_DN_OF_THE_USER_YOU_WILL_BIND_WITH',
# :password => 'THE_PASSWORD_OF_THE_BIND_USER'
end
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
config_file = File.join(rails_root, 'config', 'resque.yml')
if File.exists?(config_file)
resque_config = YAML.load_file(config_file)
Resque.redis = resque_config[rails_env]
end
development: localhost:6379
test: localhost:6379
production: redis.example.com:6379
...@@ -10,7 +10,7 @@ Gitlab::Application.routes.draw do ...@@ -10,7 +10,7 @@ Gitlab::Application.routes.draw do
# Optionally, enable Resque here # Optionally, enable Resque here
require 'resque/server' require 'resque/server'
mount Resque::Server.new, at: '/info/resque', as: 'resque' mount Resque::Server => '/info/resque', as: 'resque'
# Enable Grack support # Enable Grack support
mount Grack::Bundle.new({ mount Grack::Bundle.new({
...@@ -43,19 +43,19 @@ Gitlab::Application.routes.draw do ...@@ -43,19 +43,19 @@ Gitlab::Application.routes.draw do
put :unblock put :unblock
end end
end end
resources :projects, :constraints => { :id => /[^\/]+/ } do resources :projects, constraints: { id: /[^\/]+/ } do
member do member do
get :team get :team
put :team_update put :team_update
end end
end end
resources :team_members, :only => [:edit, :update, :destroy] resources :team_members, only: [:edit, :update, :destroy]
resources :hooks, :only => [:index, :create, :destroy] do resources :hooks, only: [:index, :create, :destroy] do
get :test get :test
end end
resource :logs resource :logs, only: [:show]
resource :resque, :controller => 'resque' resource :resque, controller: 'resque', only: [:show]
root :to => "dashboard#index" root to: "dashboard#index"
end end
get "errors/githost" get "errors/githost"
...@@ -63,38 +63,39 @@ Gitlab::Application.routes.draw do ...@@ -63,38 +63,39 @@ Gitlab::Application.routes.draw do
# #
# Profile Area # Profile Area
# #
get "profile/password", :to => "profile#password" get "profile/account" => "profile#account"
put "profile/password", :to => "profile#password_update" get "profile/history" => "profile#history"
get "profile/token", :to => "profile#token" put "profile/password" => "profile#password_update"
put "profile/reset_private_token", :to => "profile#reset_private_token" get "profile/token" => "profile#token"
get "profile", :to => "profile#show" put "profile/reset_private_token" => "profile#reset_private_token"
get "profile/design", :to => "profile#design" get "profile" => "profile#show"
put "profile/update", :to => "profile#update" get "profile/design" => "profile#design"
put "profile/update" => "profile#update"
resources :keys resources :keys
# #
# Dashboard Area # Dashboard Area
# #
get "dashboard", :to => "dashboard#index" get "dashboard" => "dashboard#index"
get "dashboard/issues", :to => "dashboard#issues" get "dashboard/issues" => "dashboard#issues"
get "dashboard/merge_requests", :to => "dashboard#merge_requests" get "dashboard/merge_requests" => "dashboard#merge_requests"
resources :projects, :constraints => { :id => /[^\/]+/ }, :only => [:new, :create] resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
devise_for :users, :controllers => { :omniauth_callbacks => :omniauth_callbacks } devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks }
# #
# Project Area # Project Area
# #
resources :projects, :constraints => { :id => /[^\/]+/ }, :except => [:new, :create, :index], :path => "/" do resources :projects, constraints: { id: /[^\/]+/ }, except: [:new, :create, :index], path: "/" do
member do member do
get "team"
get "wall" get "wall"
get "graph" get "graph"
get "files" get "files"
end end
resources :wikis, :only => [:show, :edit, :destroy, :create] do resources :wikis, only: [:show, :edit, :destroy, :create] do
collection do collection do
get :pages get :pages
end end
...@@ -113,46 +114,45 @@ Gitlab::Application.routes.draw do ...@@ -113,46 +114,45 @@ Gitlab::Application.routes.draw do
end end
resources :deploy_keys resources :deploy_keys
resources :protected_branches, :only => [:index, :create, :destroy] resources :protected_branches, only: [:index, :create, :destroy]
resources :refs, :only => [], :path => "/" do resources :refs, only: [], path: "/" do
collection do collection do
get "switch" get "switch"
end end
member do member do
get "tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ } get "tree", constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }
get "logs_tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ } get "logs_tree", constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }
get "blob", get "blob",
:constraints => { constraints: {
:id => /[a-zA-Z.0-9\/_\-]+/, id: /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/ path: /.*/
} }
# tree viewer # tree viewer
get "tree/:path" => "refs#tree", get "tree/:path" => "refs#tree",
:as => :tree_file, as: :tree_file,
:constraints => { constraints: {
:id => /[a-zA-Z.0-9\/_\-]+/, id: /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/ path: /.*/
} }
# tree viewer # tree viewer
get "logs_tree/:path" => "refs#logs_tree", get "logs_tree/:path" => "refs#logs_tree",
:as => :logs_file, as: :logs_file,
:constraints => { constraints: {
:id => /[a-zA-Z.0-9\/_\-]+/, id: /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/ path: /.*/
} }
# blame # blame
get "blame/:path" => "refs#blame", get "blame/:path" => "refs#blame",
:as => :blame_file, as: :blame_file,
:constraints => { constraints: {
:id => /[a-zA-Z.0-9\/_\-]+/, id: /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/ path: /.*/
} }
end end
end end
...@@ -177,7 +177,7 @@ Gitlab::Application.routes.draw do ...@@ -177,7 +177,7 @@ Gitlab::Application.routes.draw do
end end
end end
resources :hooks, :only => [:index, :create, :destroy] do resources :hooks, only: [:index, :create, :destroy] do
member do member do
get :test get :test
end end
...@@ -191,9 +191,10 @@ Gitlab::Application.routes.draw do ...@@ -191,9 +191,10 @@ Gitlab::Application.routes.draw do
get :patch get :patch
end end
end end
resources :team, controller: 'team_members', only: [:index]
resources :team_members resources :team_members
resources :milestones resources :milestones
resources :labels, :only => [:index] resources :labels, only: [:index]
resources :issues do resources :issues do
collection do collection do
...@@ -202,11 +203,12 @@ Gitlab::Application.routes.draw do ...@@ -202,11 +203,12 @@ Gitlab::Application.routes.draw do
get :search get :search
end end
end end
resources :notes, :only => [:index, :create, :destroy] do resources :notes, only: [:index, :create, :destroy] do
collection do collection do
post :preview post :preview
end end
end end
end end
root :to => "dashboard#index"
root to: "dashboard#index"
end end
...@@ -34,3 +34,4 @@ When listing resources you can pass the following parameters: ...@@ -34,3 +34,4 @@ When listing resources you can pass the following parameters:
+ [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md)
+ [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md)
+ [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md)
+ [SSH Keys](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/keys.md)
## List keys
Get a list of currently authenticated user's keys.
```
GET /keys
```
```json
[
{
"id": 1,
"title" : "Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
},
{
"id": 3,
"title" : "Another Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
}
]
```
## Single key
Get a single key.
```
GET /keys/:id
```
Parameters:
+ `id` (required) - The ID of a key
```json
{
"id": 1,
"title" : "Public key"
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
}
```
## Add key
Create new key owned by currently authenticated user
```
POST /keys
```
Parameters:
+ `title` (required) - new SSH Key's title
+ `key` (required) - new SSH key
Will return created key with status `201 Created` on success, or `404 Not
found` on fail.
## Delete key
Delete key owned by currently authenticated user
```
DELETE /keys/:id
```
Parameters:
+ `id` (required) - key ID
Will return `200 OK` on success, or `404 Not Found` on fail.
...@@ -173,6 +173,50 @@ Parameters: ...@@ -173,6 +173,50 @@ Parameters:
Will return status `200 OK` on success, or `404 Not found` on fail. Will return status `200 OK` on success, or `404 Not found` on fail.
## Get project hooks
Get hooks for project
```
GET /projects/:id/hooks
```
Parameters:
+ `id` (required) - The ID or code name of a project
Will return hooks with status `200 OK` on success, or `404 Not found` on fail.
## Add project hook
Add hook to project
```
POST /projects/:id/hooks
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `url` (required) - The hook URL
Will return status `201 Created` on success, or `404 Not found` on fail.
## Delete project hook
Delete hook from project
```
DELETE /projects/:id/hooks
```
Parameters:
+ `id` (required) - The ID or code name of a project
+ `hook_id` (required) - The ID of hook to delete
Will return status `200 OK` on success, or `404 Not found` on fail.
## Project repository branches ## Project repository branches
Get a list of repository branches from a project, sorted by name alphabetically. Get a list of repository branches from a project, sorted by name alphabetically.
......
...@@ -36,10 +36,10 @@ ...@@ -36,10 +36,10 @@
### 3. Run Tests ### 3. Run Tests
# All in one # All in one
bundle exec gitlab:test bundle exec rake gitlab:test
# Rspec # Rspec
bundle exec rake spec bundle exec rake spec
# Cucumber # Spinach
bundle exec rake cucumber bundle exec rake spinach
...@@ -152,7 +152,14 @@ and ensure you have followed all of the above steps carefully. ...@@ -152,7 +152,14 @@ and ensure you have followed all of the above steps carefully.
sudo pip install pygments sudo pip install pygments
sudo gem install bundler sudo gem install bundler
cd /home/gitlab cd /home/gitlab
# Get gitlab code. Use this for stable setup
sudo -H -u gitlab git clone -b stable https://github.com/gitlabhq/gitlabhq.git gitlab sudo -H -u gitlab git clone -b stable https://github.com/gitlabhq/gitlabhq.git gitlab
# Skip this for stable setup.
# Master branch (recent changes, less stable)
sudo -H -u gitlab git clone -b master https://github.com/gitlabhq/gitlabhq.git gitlab
cd gitlab cd gitlab
# Rename config files # Rename config files
...@@ -244,6 +251,14 @@ You can login via web using admin generated with setup: ...@@ -244,6 +251,14 @@ You can login via web using admin generated with setup:
# if you run this as root /home/gitlab/gitlab/tmp/pids/resque_worker.pid will be owned by root # if you run this as root /home/gitlab/gitlab/tmp/pids/resque_worker.pid will be owned by root
# causing the resque worker not to start via init script on next boot/service restart # causing the resque worker not to start via init script on next boot/service restart
## Customizing Resque's Redis connection
If you'd like Resque to connect to a Redis server on a non-standard port or on
a different host, you can configure its connection string in the
**config/resque.yml** file:
production: redis.example.com:6379
**Ok - we have a working application now. ** **Ok - we have a working application now. **
**But keep going - there are some things that should be done ** **But keep going - there are some things that should be done **
...@@ -252,7 +267,7 @@ You can login via web using admin generated with setup: ...@@ -252,7 +267,7 @@ You can login via web using admin generated with setup:
## 1. Unicorn ## 1. Unicorn
cd /home/gitlab/gitlab cd /home/gitlab/gitlab
sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb sudo -u gitlab cp config/unicorn.rb.example config/unicorn.rb
sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D
## 2. Nginx ## 2. Nginx
...@@ -269,7 +284,6 @@ You can login via web using admin generated with setup: ...@@ -269,7 +284,6 @@ You can login via web using admin generated with setup:
# of the host serving GitLab. # of the host serving GitLab.
sudo vim /etc/nginx/sites-enabled/gitlab sudo vim /etc/nginx/sites-enabled/gitlab
# Restart nginx: # Restart nginx:
/etc/init.d/nginx restart /etc/init.d/nginx restart
......
Feature: Dashboard Feature: Dashboard
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And project "Shop" has push event And project "Shop" has push event
And I visit dashboard page And I visit dashboard page
...@@ -25,4 +25,3 @@ Feature: Dashboard ...@@ -25,4 +25,3 @@ Feature: Dashboard
And user with name "John Doe" left project "Shop" And user with name "John Doe" left project "Shop"
When I visit dashboard page When I visit dashboard page
Then I should see "John Doe left project Shop" event Then I should see "John Doe left project Shop" event
Feature: Dashboard Issues Feature: Dashboard Issues
Background: Background:
Given I signin as a user Given I sign in as a user
And I have assigned issues And I have assigned issues
And I visit dashboard issues page And I visit dashboard issues page
......
Feature: Dashboard MR Feature: Dashboard Merge Requests
Background: Background:
Given I signin as a user Given I sign in as a user
And I have authored merge requests And I have authored merge requests
And I visit dashboard merge requests page And I visit dashboard merge requests page
......
Feature: Dashboard Search Feature: Dashboard Search
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And I visit dashboard search page And I visit dashboard search page
Scenario: I should see project i'm looking for Scenario: I should see project I am looking for
Given I search for "Sho" Given I search for "Sho"
Then I should see "Shop" project link Then I should see "Shop" project link
Feature: Profile Feature: Profile
Background: Background:
Given I signin as a user Given I sign in as a user
Scenario: I look at my profile Scenario: I look at my profile
Given I visit profile page Given I visit profile page
...@@ -12,11 +12,11 @@ Feature: Profile ...@@ -12,11 +12,11 @@ Feature: Profile
And I should see new contact info And I should see new contact info
Scenario: I change my password Scenario: I change my password
Given I visit profile password page Given I visit profile account page
Then I change my password Then I change my password
And I should be redirected to sign in page And I should be redirected to sign in page
Scenario: I reset my token Scenario: I reset my token
Given I visit profile token page Given I visit profile account page
Then I reset my token Then I reset my token
And I should see new token And I should see new token
Feature: SSH Keys Feature: Profile SSH Keys
Background: Background:
Given I signin as a user Given I sign in as a user
And I have ssh keys: And I have ssh key "ssh-rsa Work"
| title |
| ssh-rsa Work |
| ssh-rsa Home |
And I visit profile keys page And I visit profile keys page
Scenario: I should see SSH keys Scenario: I should see ssh keys
Then I should see my ssh keys Then I should see my ssh keys
Scenario: Add new ssh key Scenario: Add new ssh key
......
Feature: Browse branches Feature: Project Browse branches
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And project "Shop" has protected branches And project "Shop" has protected branches
Given I visit project branches page Given I visit project branches page
...@@ -16,8 +16,11 @@ Feature: Browse branches ...@@ -16,8 +16,11 @@ Feature: Browse branches
Given I click link "Protected" Given I click link "Protected"
Then I should see "Shop" protected branches list Then I should see "Shop" protected branches list
Scenario: I can download project by branch # @wip
# Scenario: I can download project by branch
Scenario: I can view protected branches # @wip
# Scenario: I can view protected branches
Scenario: I can manage protected branches # @wip
# Scenario: I can manage protected branches
Feature: Comment commit Feature: Project Comment commit
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
Given I visit project commit page Given I visit project commit page
......
Feature: Browse commits Feature: Project Browse commits
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
Given I visit project commits page Given I visit project commits page
...@@ -19,4 +19,3 @@ Feature: Browse commits ...@@ -19,4 +19,3 @@ Feature: Browse commits
Given I visit compare refs page Given I visit compare refs page
And I fill compare fields with refs And I fill compare fields with refs
And I see compared refs And I see compared refs
Feature: Browse tags Feature: Project Browse tags
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
Given I visit project tags page Given I visit project tags page
Scenario: I can see all git tags Scenario: I can see all git tags
Then I should see "Shop" all tags list Then I should see "Shop" all tags list
Scenario: I can download project by tag # @wip
# Scenario: I can download project by tag
...@@ -4,7 +4,7 @@ Feature: Create Project ...@@ -4,7 +4,7 @@ Feature: Create Project
Should be able to create a new one Should be able to create a new one
Scenario: User create a project Scenario: User create a project
Given I signin as a user Given I sign in as a user
When I visit new project page When I visit new project page
And fill project form with valid data And fill project form with valid data
Then I should see project page Then I should see project page
......
Feature: Issues Feature: Project Issues
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And project "Shop" have "Release 0.4" open issue And project "Shop" have "Release 0.4" open issue
And project "Shop" have "Release 0.3" closed issue And project "Shop" have "Release 0.3" closed issue
...@@ -79,4 +79,3 @@ Feature: Issues ...@@ -79,4 +79,3 @@ Feature: Issues
When I select first assignee from "Shop" project When I select first assignee from "Shop" project
And I click link "New Issue" And I click link "New Issue"
Then I should see first assignee from "Shop" as selected assignee Then I should see first assignee from "Shop" as selected assignee
Feature: Labels Feature: Project Labels
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And project "Shop" have issues tags: And project "Shop" have issues tags: "bug", "feature"
| name |
| bug |
| feature |
Given I visit project "Shop" labels page Given I visit project "Shop" labels page
Scenario: I should see active milestones Scenario: I should see active milestones
......
Feature: Milestones Feature: Project Milestones
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And project "Shop" has milestone "v2.2" And project "Shop" has milestone "v2.2"
Given I visit project "Shop" milestones page Given I visit project "Shop" milestones page
......
Feature: Merge Requests Feature: Project Merge Requests
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And project "Shop" have "Bug NS-04" open merge request And project "Shop" have "Bug NS-04" open merge request
And project "Shop" have "Feature NS-03" closed merge request And project "Shop" have "Feature NS-03" closed merge request
......
@javascript
Feature: Project Network Graph Feature: Project Network Graph
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And I visit project "Shop" network page And I visit project "Shop" network page
@javascript
Scenario: I should see project network Scenario: I should see project network
Then page should have network graph Then page should have network graph
Feature: Projects
Background:
Given I signin as a user
And I own project "Shop"
And I visit project "Shop" page
# @wip
# Scenario: I should see project activity
# @wip
# Scenario: I edit project
# @wip
# Scenario: I visit attachments
Feature: Browse git repo Feature: Project Browse files
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
Given I visit project source page Given I visit project source page
...@@ -12,12 +12,10 @@ Feature: Browse git repo ...@@ -12,12 +12,10 @@ Feature: Browse git repo
Then I should see files from repository for "8470d70" Then I should see files from repository for "8470d70"
Scenario: I browse file content Scenario: I browse file content
Given I click on file from repo Given I click on "Gemfile" file in repo
Then I should see it content Then I should see it content
Scenario: I browse raw file Scenario: I browse raw file
Given I visit blob file from repo Given I visit blob file from repo
And I click on raw button And I click link "raw"
Then I should see raw file content Then I should see raw file content
Feature: Browse git repo Feature: Project Browse git repo
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
Given I visit project source page Given I visit project source page
Scenario: I blame file Scenario: I blame file
Given I click on file from repo Given I click on "Gemfile" file in repo
And I click blame button And I click blame button
Then I should see git file blame Then I should see git file blame
Feature: Project Team management Feature: Project Team management
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And gitlab user "Mike" And gitlab user "Mike"
And gitlab user "Sam" And gitlab user "Sam"
...@@ -32,4 +32,3 @@ Feature: Project Team management ...@@ -32,4 +32,3 @@ Feature: Project Team management
And I click link "Remove from team" And I click link "Remove from team"
Then I visit project "Shop" team page Then I visit project "Shop" team page
And I should not see "Sam" in team list And I should not see "Sam" in team list
@javascript
Feature: Project Wall Feature: Project Wall
In order to use Project Wall In order to use Project Wall
A user A user should be able to read and write messages
Should be able to read & write messages
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
And I visit project "Shop" wall page And I visit project "Shop" wall page
@javascript
Scenario: Write comment Scenario: Write comment
Given I write new comment "my special test message" Given I write new comment "my special test message"
Then I should see project wall note "my special test message" Then I should see project wall note "my special test message"
......
Feature: Wiki Feature: Project Wiki
Background: Background:
Given I signin as a user Given I sign in as a user
And I own project "Shop" And I own project "Shop"
Given I visit project wiki page Given I visit project wiki page
......
Feature: Project
Background:
Given I signin as a user
And I own project "Shop"
And I visit project "Shop" page
Scenario: I should see project activity
Scenario: I edit project
Scenario: I visit attachments
include LoginHelpers
Given /^I signin as a user$/ do
login_as :user
end
When /^I click link "(.*?)"$/ do |link|
click_link link
end
When /^I click button "(.*?)"$/ do |button|
click_button button
end
When /^I fill in "(.*?)" with "(.*?)"$/ do |field, value|
fill_in field, :with => value
end
Given /^show me page$/ do
save_and_open_page
end
Then /^I should see "(.*?)" link$/ do |arg1|
page.should have_link(arg1)
end
Then /^I should see "(.*?)" project link$/ do |arg1|
page.should have_link(arg1)
end
Then /^I should see project "(.*?)" activity feed$/ do |arg1|
project = Project.find_by_name(arg1)
page.should have_content "#{@user.name} pushed new branch new_design at #{project.name}"
end
Given /^project "(.*?)" has push event$/ do |arg1|
@project = Project.find_by_name(arg1)
data = {
:before => "0000000000000000000000000000000000000000",
:after => "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
:ref => "refs/heads/new_design",
:user_id => @user.id,
:user_name => @user.name,
:repository => {
:name => @project.name,
:url => "localhost/rubinius",
:description => "",
:homepage => "localhost/rubinius",
:private => true
}
}
@event = Event.create(
:project => @project,
:action => Event::Pushed,
:data => data,
:author_id => @user.id
)
end
Then /^I should see last push widget$/ do
page.should have_content "Your pushed to branch new_design"
page.should have_link "Create Merge Request"
end
Then /^I click "(.*?)" link$/ do |arg1|
click_link arg1 #Create Merge Request"
end
Then /^I see prefilled new Merge Request page$/ do
current_path.should == new_project_merge_request_path(@project)
find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design"
end
Given /^I visit dashboard search page$/ do
visit search_path
end
Given /^I search for "(.*?)"$/ do |arg1|
fill_in "dashboard_search", :with => arg1
click_button "Search"
end
Then /^I should see issues assigned to me$/ do
issues = @user.issues
issues.each do |issue|
page.should have_content(issue.title[0..10])
page.should have_content(issue.project.name)
end
end
Then /^I should see my merge requests$/ do
merge_requests = @user.merge_requests
merge_requests.each do |mr|
page.should have_content(mr.title[0..10])
page.should have_content(mr.project.name)
end
end
Given /^I have assigned issues$/ do
project = Factory :project
project.add_access(@user, :read, :write)
issue1 = Factory :issue,
:author => @user,
:assignee => @user,
:project => project
issue2 = Factory :issue,
:author => @user,
:assignee => @user,
:project => project
end
Given /^I have authored merge requests$/ do
project1 = Factory :project
project2 = Factory :project
project1.add_access(@user, :read, :write)
project2.add_access(@user, :read, :write)
merge_request1 = Factory :merge_request,
:author => @user,
:project => project1
merge_request2 = Factory :merge_request,
:author => @user,
:project => project2
end
Given /^user with name "(.*?)" joined project "(.*?)"$/ do |user_name, project_name|
user = Factory.create(:user, {name: user_name})
project = Project.find_by_name project_name
Event.create(
project: project,
author_id: user.id,
action: Event::Joined
)
end
Given /^user with name "(.*?)" left project "(.*?)"$/ do |user_name, project_name|
user = User.find_by_name user_name
project = Project.find_by_name project_name
Event.create(
project: project,
author_id: user.id,
action: Event::Left
)
end
Then /^I should see "(.*?)" event$/ do |event_text|
page.should have_content(event_text)
end
Given /^I visit profile keys page$/ do
visit keys_path
end
Then /^I should see my ssh keys$/ do
@user.keys.each do |key|
page.should have_content(key.title)
end
end
Given /^I have ssh keys:$/ do |table|
table.hashes.each do |row|
Factory :key, :user => @user, :title => row[:title], :key => "jfKLJDFKSFJSHFJ#{row[:title]}"
end
end
Given /^I submit new ssh key "(.*?)"$/ do |arg1|
fill_in "key_title", :with => arg1
fill_in "key_key", :with => "ssh-rsa publickey234="
click_button "Save"
end
Then /^I should see new ssh key "(.*?)"$/ do |arg1|
key = Key.find_by_title(arg1)
page.should have_content(key.title)
page.should have_content(key.key)
current_path.should == key_path(key)
end
Then /^I should not see "(.*?)" ssh key$/ do |arg1|
within "#keys-table" do
page.should_not have_content(arg1)
end
end
Then /^I should see my profile info$/ do
page.should have_content "Profile"
page.should have_content @user.name
page.should have_content @user.email
end
Then /^I change my password$/ do
fill_in "user_password", :with => "222333"
fill_in "user_password_confirmation", :with => "222333"
click_button "Save"
end
Then /^I should be redirected to sign in page$/ do
current_path.should == new_user_session_path
end
Then /^I reset my token$/ do
@old_token = @user.private_token
click_button "Reset"
end
Then /^I should see new token$/ do
find("#token").value.should_not == @old_token
find("#token").value.should == @user.reload.private_token
end
Then /^I change my contact info$/ do
fill_in "user_skype", :with => "testskype"
fill_in "user_linkedin", :with => "testlinkedin"
fill_in "user_twitter", :with => "testtwitter"
click_button "Save"
@user.reload
end
Then /^I should see new contact info$/ do
@user.skype.should == 'testskype'
@user.linkedin.should == 'testlinkedin'
@user.twitter.should == 'testtwitter'
end
Then /^I should see files from repository$/ do
page.should have_content("app")
page.should have_content("History")
page.should have_content("Gemfile")
end
Then /^I should see files from repository for "(.*?)"$/ do |arg1|
current_path.should == tree_project_ref_path(@project, arg1)
page.should have_content("app")
page.should have_content("History")
page.should have_content("Gemfile")
end
Given /^I click on file from repo$/ do
click_link "Gemfile"
end
Then /^I should see it content$/ do
page.should have_content("rubygems.org")
end
Given /^I click on raw button$/ do
click_link "raw"
end
Then /^I should see raw file content$/ do
page.source.should == ValidCommit::BLOB_FILE
end
Given /^I click blame button$/ do
click_link "blame"
end
Then /^I should see git file blame$/ do
page.should have_content("rubygems.org")
page.should have_content("Dmitriy Zaporozhets")
page.should have_content("bc3735004cb Moving to rails 3.2")
end
Then /^I see project commits$/ do
current_path.should == project_commits_path(@project)
commit = @project.commit
page.should have_content(@project.name)
page.should have_content(commit.message)
page.should have_content(commit.id.to_s[0..5])
end
Given /^I click atom feed link$/ do
click_link "Feed"
end
Then /^I see commits atom feed$/ do
commit = CommitDecorator.decorate(@project.commit)
page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", :text => "Recent commits to #{@project.name}")
page.body.should have_selector("author email", :text => commit.author_email)
page.body.should have_selector("entry summary", :text => commit.description)
end
Then /^I see commit info$/ do
page.should have_content ValidCommit::MESSAGE
page.should have_content "Showing 1 changed file"
end
Given /^I fill compare fields with refs$/ do
fill_in "from", :with => "master"
fill_in "to", :with => "stable"
click_button "Compare"
end
Given /^I see compared refs$/ do
page.should have_content "Commits (27)"
page.should have_content "Compare View"
page.should have_content "Showing 73 changed files"
end
Then /^I should see "(.*?)" recent branches list$/ do |arg1|
page.should have_content("Branches")
page.should have_content("master")
end
Then /^I should see "(.*?)" all branches list$/ do |arg1|
page.should have_content("Branches")
page.should have_content("master")
end
Then /^I should see "(.*?)" all tags list$/ do |arg1|
page.should have_content("Tags")
page.should have_content("v1.2.1")
end
Then /^I should see "(.*?)" protected branches list$/ do |arg1|
within "table" do
page.should have_content "stable"
page.should_not have_content "master"
end
end
Given /^project "(.*?)" has protected branches$/ do |arg1|
project = Project.find_by_name(arg1)
project.protected_branches.create(:name => "stable")
end
Given /^project "(.*?)" have "(.*?)" open issue$/ do |arg1, arg2|
project = Project.find_by_name(arg1)
Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first)
end
Given /^project "(.*?)" have "(.*?)" closed issue$/ do |arg1, arg2|
project = Project.find_by_name(arg1)
Factory.create(:issue, :title => arg2, :project => project, :author => project.users.first, :closed => true)
end
Given /^I should see "(.*?)" in issues$/ do |arg1|
page.should have_content arg1
end
Given /^I should not see "(.*?)" in issues$/ do |arg1|
page.should_not have_content arg1
end
Then /^I should see issue "(.*?)"$/ do |arg1|
issue = Issue.find_by_title(arg1)
page.should have_content issue.title
page.should have_content issue.author_name
page.should have_content issue.project.name
end
Given /^I submit new issue "(.*?)"$/ do |arg1|
fill_in "issue_title", with: arg1
click_button "Submit new issue"
end
Given /^project "(.*?)" have issues tags:$/ do |arg1, table|
project = Project.find_by_name(arg1)
table.hashes.each do |hash|
Factory :issue,
project: project,
label_list: [hash[:name]]
end
end
Given /^I visit project "(.*?)" labels page$/ do |arg1|
visit project_labels_path(Project.find_by_name(arg1))
end
Then /^I should see label "(.*?)"$/ do |arg1|
within ".labels-table" do
page.should have_content arg1
end
end
Given /^I fill in issue search with "(.*?)"$/ do |arg1|
# Because fill_in, with: "" triggers nothing
# we need to trigger a keyup event
if arg1 == ''
page.execute_script("$('.issue_search').val('').keyup();");
end
fill_in 'issue_search', with: arg1
end
When /^I select milestone "(.*?)"$/ do |milestone_title|
select milestone_title, from: "milestone_id"
end
Then /^I should see selected milestone with title "(.*?)"$/ do |milestone_title|
issues_milestone_selector = "#issue_milestone_id_chzn/a"
wait_until{ page.has_content?("Details") }
page.find(issues_milestone_selector).should have_content(milestone_title)
end
When /^I select first assignee from "(.*?)" project$/ do |project_name|
project = Project.find_by_name project_name
first_assignee = project.users.first
select first_assignee.name, from: "assignee_id"
end
Then /^I should see first assignee from "(.*?)" as selected assignee$/ do |project_name|
issues_assignee_selector = "#issue_assignee_id_chzn/a"
wait_until{ page.has_content?("Details") }
project = Project.find_by_name project_name
assignee_name = project.users.first.name
page.find(issues_assignee_selector).should have_content(assignee_name)
end
Given /^project "(.*?)" have "(.*?)" open merge request$/ do |arg1, arg2|
project = Project.find_by_name(arg1)
Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first)
end
Given /^project "(.*?)" have "(.*?)" closed merge request$/ do |arg1, arg2|
project = Project.find_by_name(arg1)
Factory.create(:merge_request, :title => arg2, :project => project, :author => project.users.first, :closed => true)
end
Then /^I should see "(.*?)" in merge requests$/ do |arg1|
page.should have_content arg1
end
Then /^I should not see "(.*?)" in merge requests$/ do |arg1|
page.should_not have_content arg1
end
Then /^I should see merge request "(.*?)"$/ do |arg1|
merge_request = MergeRequest.find_by_title(arg1)
page.should have_content(merge_request.title[0..10])
page.should have_content(merge_request.target_branch)
page.should have_content(merge_request.source_branch)
end
Given /^I submit new merge request "(.*?)"$/ do |arg1|
fill_in "merge_request_title", :with => arg1
select "master", :from => "merge_request_source_branch"
select "stable", :from => "merge_request_target_branch"
click_button "Save"
end
Then /^I should see closed merge request "(.*?)"$/ do |arg1|
mr = MergeRequest.find_by_title(arg1)
mr.closed.should be_true
page.should have_content "Closed by"
end
Given /^project "(.*?)" has milestone "(.*?)"$/ do |arg1, arg2|
project = Project.find_by_name(arg1)
milestone = Factory :milestone,
:title => arg2,
:project => project
3.times do |i|
issue = Factory :issue,
:project => project,
:milestone => milestone
end
end
Then /^I should see active milestones$/ do
milestone = @project.milestones.first
page.should have_content(milestone.title[0..10])
page.should have_content(milestone.expires_at)
page.should have_content("Browse Issues")
end
Then /^I should see milestone "(.*?)"$/ do |arg1|
milestone = @project.milestones.find_by_title(arg1)
page.should have_content(milestone.title[0..10])
page.should have_content(milestone.expires_at)
page.should have_content("Browse Issues")
end
Given /^I submit new milestone "(.*?)"$/ do |arg1|
fill_in "milestone_title", :with => arg1
click_button "Create milestone"
end
Given /^gitlab user "(.*?)"$/ do |arg1|
Factory :user, :name => arg1
end
Given /^"(.*?)" is "(.*?)" developer$/ do |arg1, arg2|
user = User.find_by_name(arg1)
project = Project.find_by_name(arg2)
project.add_access(user, :write)
end
Then /^I should be able to see myself in team$/ do
page.should have_content(@user.name)
page.should have_content(@user.email)
end
Then /^I should see "(.*?)" in team list$/ do |arg1|
user = User.find_by_name(arg1)
page.should have_content(user.name)
page.should have_content(user.email)
end
Given /^I select "(.*?)" as "(.*?)"$/ do |arg1, arg2|
user = User.find_by_name(arg1)
within "#new_team_member" do
select user.name, :from => "user_ids"
select arg2, :from => "project_access"
end
click_button "Save"
end
Then /^I should see "(.*?)" in team list as "(.*?)"$/ do |arg1, arg2|
user = User.find_by_name(arg1)
role_id = find(".user_#{user.id} #team_member_project_access").value
role_id.should == UsersProject.access_roles[arg2].to_s
end
Given /^I change "(.*?)" role to "(.*?)"$/ do |arg1, arg2|
user = User.find_by_name(arg1)
within ".user_#{user.id}" do
select arg2, :from => "team_member_project_access"
end
end
Then /^I should see "(.*?)" team profile$/ do |arg1|
user = User.find_by_name(arg1)
page.should have_content(user.name)
page.should have_content(user.email)
page.should have_content("To team list")
end
Then /^I should not see "(.*?)" in team list$/ do |arg1|
user = User.find_by_name(arg1)
page.should_not have_content(user.name)
page.should_not have_content(user.email)
end
Given /^I create Wiki page$/ do
fill_in "Title", :with => 'Test title'
fill_in "Content", :with => '[link test](test)'
click_on "Save"
end
Then /^I should see newly created wiki page$/ do
page.should have_content("Test title")
page.should have_content("link test")
click_link "link test"
page.should have_content("Editing page")
end
When /^I visit new project page$/ do
visit new_project_path
end
When /^fill project form with valid data$/ do
fill_in 'project_name', :with => 'NewProject'
fill_in 'project_code', :with => 'NPR'
fill_in 'project_path', :with => 'newproject'
click_button "Create project"
end
Then /^I should see project page$/ do
current_path.should == project_path(Project.last)
page.should have_content('NewProject')
end
Then /^I should see empty project instuctions$/ do
page.should have_content("git init")
page.should have_content("git remote")
page.should have_content(Project.last.url_to_repo)
end
Given /^I own project "(.*?)"$/ do |arg1|
@project = Factory :project, :name => arg1
@project.add_access(@user, :admin)
end
Given /^I visit project "(.*?)" wall page$/ do |arg1|
project = Project.find_by_name(arg1)
visit wall_project_path(project)
end
Then /^I should see project wall note "(.*?)"$/ do |arg1|
page.should have_content arg1
end
Given /^project "(.*?)" has comment "(.*?)"$/ do |arg1, arg2|
project = Project.find_by_name(arg1)
project.notes.create(:note => arg1, :author => project.users.first)
end
Given /^I write new comment "(.*?)"$/ do |arg1|
fill_in "note_note", :with => arg1
click_button "Add Comment"
end
Given /^I visit project "(.*?)" page$/ do |arg1|
project = Project.find_by_name(arg1)
visit project_path(project)
end
Given /^I visit project "(.*?)" network page$/ do |arg1|
project = Project.find_by_name(arg1)
# Stub out find_all to speed this up (10 commits vs. 650)
commits = Grit::Commit.find_all(project.repo, nil, {max_count: 10})
Grit::Commit.stub(:find_all).and_return(commits)
visit graph_project_path(project)
end
Given /^page should have network graph$/ do
page.should have_content "Project Network Graph"
within ".graph" do
page.should have_content "master"
page.should have_content "scss_refactor..."
end
end
Given /^I leave a comment like "(.*?)"$/ do |arg1|
fill_in "note_note", :with => arg1
click_button "Add Comment"
end
Then /^I should see comment "(.*?)"$/ do |arg1|
page.should have_content(arg1)
end
Given /^I visit project "(.*?)" issues page$/ do |arg1|
visit project_issues_path(Project.find_by_name(arg1))
end
Given /^I visit issue page "(.*?)"$/ do |arg1|
issue = Issue.find_by_title(arg1)
visit project_issue_path(issue.project, issue)
end
Given /^I visit project "(.*?)" merge requests page$/ do |arg1|
visit project_merge_requests_path(Project.find_by_name(arg1))
end
Given /^I visit merge request page "(.*?)"$/ do |arg1|
mr = MergeRequest.find_by_title(arg1)
visit project_merge_request_path(mr.project, mr)
end
Given /^I visit project "(.*?)" milestones page$/ do |arg1|
@project = Project.find_by_name(arg1)
visit project_milestones_path(@project)
end
Given /^I visit project commits page$/ do
visit project_commits_path(@project)
end
Given /^I visit compare refs page$/ do
visit compare_project_commits_path(@project)
end
Given /^I visit project branches page$/ do
visit branches_project_repository_path(@project)
end
Given /^I visit project commit page$/ do
visit project_commit_path(@project, ValidCommit::ID)
end
Given /^I visit project tags page$/ do
visit tags_project_repository_path(@project)
end
Given /^I click on commit link$/ do
visit project_commit_path(@project, ValidCommit::ID)
end
Given /^I visit project source page$/ do
visit tree_project_ref_path(@project, @project.root_ref)
end
Given /^I visit project source page for "(.*?)"$/ do |arg1|
visit tree_project_ref_path(@project, arg1)
end
Given /^I visit blob file from repo$/ do
visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH)
end
Given /^I visit project "(.*?)" team page$/ do |arg1|
visit team_project_path(Project.find_by_name(arg1))
end
Given /^I visit project wiki page$/ do
visit project_wiki_path(@project, :index)
end
Given /^I visit profile page$/ do
visit profile_path
end
Given /^I visit profile token page$/ do
visit profile_token_path
end
Given /^I visit profile password page$/ do
visit profile_password_path
end
Given /^I visit dashboard page$/ do
visit dashboard_path
end
Given /^I visit dashboard issues page$/ do
visit dashboard_issues_path
end
Given /^I visit dashboard merge requests page$/ do
visit dashboard_merge_requests_path
end
class Dashboard < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
Then 'I should see "New Project" link' do
page.should have_link "New Project"
end
Then 'I should see "Shop" project link' do
page.should have_link "Shop"
end
Then 'I should see project "Shop" activity feed' do
project = Project.find_by_name("Shop")
page.should have_content "#{@user.name} pushed new branch new_design at #{project.name}"
end
Then 'I should see last push widget' do
page.should have_content "Your pushed to branch new_design"
page.should have_link "Create Merge Request"
end
And 'I click "Create Merge Request" link' do
click_link "Create Merge Request"
end
Then 'I see prefilled new Merge Request page' do
current_path.should == new_project_merge_request_path(@project)
find("#merge_request_source_branch").value.should == "new_design"
find("#merge_request_target_branch").value.should == "master"
find("#merge_request_title").value.should == "New Design"
end
Given 'user with name "John Doe" joined project "Shop"' do
user = Factory.create(:user, {name: "John Doe"})
project = Project.find_by_name "Shop"
Event.create(
project: project,
author_id: user.id,
action: Event::Joined
)
end
Then 'I should see "John Doe joined project Shop" event' do
page.should have_content "John Doe joined project Shop"
end
And 'user with name "John Doe" left project "Shop"' do
user = User.find_by_name "John Doe"
project = Project.find_by_name "Shop"
Event.create(
project: project,
author_id: user.id,
action: Event::Left
)
end
Then 'I should see "John Doe left project Shop" event' do
page.should have_content "John Doe left project Shop"
end
And 'I own project "Shop"' do
@project = Factory :project, :name => 'Shop'
@project.add_access(@user, :admin)
end
And 'project "Shop" has push event' do
@project = Project.find_by_name("Shop")
data = {
:before => "0000000000000000000000000000000000000000",
:after => "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
:ref => "refs/heads/new_design",
:user_id => @user.id,
:user_name => @user.name,
:repository => {
:name => @project.name,
:url => "localhost/rubinius",
:description => "",
:homepage => "localhost/rubinius",
:private => true
}
}
@event = Event.create(
:project => @project,
:action => Event::Pushed,
:data => data,
:author_id => @user.id
)
end
end
class DashboardIssues < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
Then 'I should see issues assigned to me' do
issues = @user.issues
issues.each do |issue|
page.should have_content(issue.title[0..10])
page.should have_content(issue.project.name)
end
end
And 'I have assigned issues' do
project = Factory :project
project.add_access(@user, :read, :write)
2.times { Factory :issue, :author => @user, :assignee => @user, :project => project }
end
end
class DashboardMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
Then 'I should see my merge requests' do
merge_requests = @user.merge_requests
merge_requests.each do |mr|
page.should have_content(mr.title[0..10])
page.should have_content(mr.project.name)
end
end
And 'I have authored merge requests' do
project1 = Factory :project
project2 = Factory :project
project1.add_access(@user, :read, :write)
project2.add_access(@user, :read, :write)
merge_request1 = Factory :merge_request, :author => @user, :project => project1
merge_request2 = Factory :merge_request, :author => @user, :project => project2
end
end
class DashboardSearch < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
Given 'I search for "Sho"' do
fill_in "dashboard_search", :with => "Sho"
click_button "Search"
end
Then 'I should see "Shop" project link' do
page.should have_link "Shop"
end
And 'I own project "Shop"' do
@project = Factory :project, :name => "Shop"
@project.add_access(@user, :admin)
end
end
class Profile < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
Then 'I should see my profile info' do
page.should have_content "Profile"
page.should have_content @user.name
page.should have_content @user.email
end
Then 'I change my contact info' do
fill_in "user_skype", :with => "testskype"
fill_in "user_linkedin", :with => "testlinkedin"
fill_in "user_twitter", :with => "testtwitter"
click_button "Save"
@user.reload
end
And 'I should see new contact info' do
@user.skype.should == 'testskype'
@user.linkedin.should == 'testlinkedin'
@user.twitter.should == 'testtwitter'
end
Then 'I change my password' do
fill_in "user_password", :with => "222333"
fill_in "user_password_confirmation", :with => "222333"
click_button "Save"
end
And 'I should be redirected to sign in page' do
current_path.should == new_user_session_path
end
Then 'I reset my token' do
@old_token = @user.private_token
click_button "Reset"
end
And 'I should see new token' do
find("#token").value.should_not == @old_token
find("#token").value.should == @user.reload.private_token
end
end
class ProfileSshKeys < Spinach::FeatureSteps
include SharedAuthentication
Then 'I should see my ssh keys' do
@user.keys.each do |key|
page.should have_content(key.title)
end
end
Given 'I click link "Add new"' do
click_link "Add new"
end
And 'I submit new ssh key "Laptop"' do
fill_in "key_title", :with => "Laptop"
fill_in "key_key", :with => "ssh-rsa publickey234="
click_button "Save"
end
Then 'I should see new ssh key "Laptop"' do
key = Key.find_by_title("Laptop")
page.should have_content(key.title)
page.should have_content(key.key)
current_path.should == key_path(key)
end
Given 'I click link "Work"' do
click_link "Work"
end
And 'I click link "Remove"' do
click_link "Remove"
end
Then 'I visit profile keys page' do
visit keys_path
end
And 'I should not see "Work" ssh key' do
within "#keys-table" do
page.should_not have_content "Work"
end
end
And 'I have ssh key "ssh-rsa Work"' do
Factory :key, :user => @user, :title => "ssh-rsa Work", :key => "jfKLJDFKSFJSHFJssh-rsa Work"
end
end
class CreateProject < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
And 'fill project form with valid data' do
fill_in 'project_name', :with => 'NewProject'
fill_in 'project_code', :with => 'NPR'
fill_in 'project_path', :with => 'newproject'
click_button "Create project"
end
Then 'I should see project page' do
current_path.should == project_path(Project.last)
page.should have_content "NewProject"
end
And 'I should see empty project instuctions' do
page.should have_content "git init"
page.should have_content "git remote"
page.should have_content Project.last.url_to_repo
end
end
class Projects < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
end
class ProjectBrowseBranches < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should see "Shop" recent branches list' do
page.should have_content "Branches"
page.should have_content "master"
end
Given 'I click link "All"' do
click_link "All"
end
Then 'I should see "Shop" all branches list' do
page.should have_content "Branches"
page.should have_content "master"
end
Given 'I click link "Protected"' do
click_link "Protected"
end
Then 'I should see "Shop" protected branches list' do
within "table" do
page.should have_content "stable"
page.should_not have_content "master"
end
end
And 'project "Shop" has protected branches' do
project = Project.find_by_name("Shop")
project.protected_branches.create(:name => "stable")
end
end
class ProjectBrowseCommits < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I see project commits' do
current_path.should == project_commits_path(@project)
commit = @project.commit
page.should have_content(@project.name)
page.should have_content(commit.message)
page.should have_content(commit.id.to_s[0..5])
end
Given 'I click atom feed link' do
click_link "Feed"
end
Then 'I see commits atom feed' do
commit = CommitDecorator.decorate(@project.commit)
page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", :text => "Recent commits to #{@project.name}")
page.body.should have_selector("author email", :text => commit.author_email)
page.body.should have_selector("entry summary", :text => commit.description)
end
Given 'I click on commit link' do
visit project_commit_path(@project, ValidCommit::ID)
end
Then 'I see commit info' do
page.should have_content ValidCommit::MESSAGE
page.should have_content "Showing 1 changed file"
end
And 'I fill compare fields with refs' do
fill_in "from", :with => "master"
fill_in "to", :with => "stable"
click_button "Compare"
end
And 'I see compared refs' do
page.should have_content "Commits (27)"
page.should have_content "Compare View"
page.should have_content "Showing 73 changed files"
end
end
class ProjectBrowseFiles < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should see files from repository' do
page.should have_content "app"
page.should have_content "History"
page.should have_content "Gemfile"
end
Then 'I should see files from repository for "8470d70"' do
current_path.should == tree_project_ref_path(@project, "8470d70")
page.should have_content "app"
page.should have_content "History"
page.should have_content "Gemfile"
end
Given 'I click on "Gemfile" file in repo' do
click_link "Gemfile"
end
Then 'I should see it content' do
page.should have_content "rubygems.org"
end
And 'I click link "raw"' do
click_link "raw"
end
Then 'I should see raw file content' do
page.source.should == ValidCommit::BLOB_FILE
end
end
class ProjectBrowseGitRepo < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Given 'I click on "Gemfile" file in repo' do
click_link "Gemfile"
end
And 'I click blame button' do
click_link "blame"
end
Then 'I should see git file blame' do
page.should have_content "rubygems.org"
page.should have_content "Dmitriy Zaporozhets"
page.should have_content "bc3735004cb Moving to rails 3.2"
end
end
class ProjectBrowseTags < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should see "Shop" all tags list' do
page.should have_content "Tags"
page.should have_content "v1.2.1"
end
end
class ProjectCommentCommit < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
end
class ProjectIssues < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
Given 'I should see "Release 0.4" in issues' do
page.should have_content "Release 0.4"
end
And 'I should not see "Release 0.3" in issues' do
page.should_not have_content "Release 0.3"
end
Given 'I click link "Closed"' do
click_link "Closed"
end
Then 'I should see "Release 0.3" in issues' do
page.should have_content "Release 0.3"
end
And 'I should not see "Release 0.4" in issues' do
page.should_not have_content "Release 0.4"
end
Given 'I click link "All"' do
click_link "All"
end
Given 'I click link "Release 0.4"' do
click_link "Release 0.4"
end
Then 'I should see issue "Release 0.4"' do
page.should have_content "Release 0.4"
end
Given 'I click link "New Issue"' do
click_link "New Issue"
end
And 'I submit new issue "500 error on profile"' do
fill_in "issue_title", :with => "500 error on profile"
click_button "Submit new issue"
end
Given 'I click link "500 error on profile"' do
click_link "500 error on profile"
end
Then 'I should see issue "500 error on profile"' do
issue = Issue.find_by_title("500 error on profile")
page.should have_content issue.title
page.should have_content issue.author_name
page.should have_content issue.project.name
end
Given 'I fill in issue search with "Release"' do
fill_in 'issue_search', with: "Release"
end
Given 'I fill in issue search with "Bug"' do
fill_in 'issue_search', with: "Bug"
end
And 'I fill in issue search with "0.3"' do
fill_in 'issue_search', with: "0.3"
end
And 'I fill in issue search with "Something"' do
fill_in 'issue_search', with: "Something"
end
And 'I fill in issue search with ""' do
page.execute_script("$('.issue_search').val('').keyup();");
fill_in 'issue_search', with: ""
end
Given 'project "Shop" has milestone "v2.2"' do
project = Project.find_by_name("Shop")
milestone = Factory :milestone, :title => "v2.2", :project => project
3.times { Factory :issue, :project => project, :milestone => milestone }
end
And 'project "Shop" has milestone "v3.0"' do
project = Project.find_by_name("Shop")
milestone = Factory :milestone, :title => "v3.0", :project => project
3.times { Factory :issue, :project => project, :milestone => milestone }
end
When 'I select milestone "v3.0"' do
select "v3.0", from: "milestone_id"
end
Then 'I should see selected milestone with title "v3.0"' do
issues_milestone_selector = "#issue_milestone_id_chzn/a"
wait_until { page.has_content?("Details") }
page.find(issues_milestone_selector).should have_content("v3.0")
end
When 'I select first assignee from "Shop" project' do
project = Project.find_by_name "Shop"
first_assignee = project.users.first
select first_assignee.name, from: "assignee_id"
end
Then 'I should see first assignee from "Shop" as selected assignee' do
issues_assignee_selector = "#issue_assignee_id_chzn/a"
wait_until { page.has_content?("Details") }
project = Project.find_by_name "Shop"
assignee_name = project.users.first.name
page.find(issues_assignee_selector).should have_content(assignee_name)
end
And 'project "Shop" have "Release 0.4" open issue' do
project = Project.find_by_name("Shop")
Factory.create(:issue,
:title => "Release 0.4",
:project => project,
:author => project.users.first)
end
And 'project "Shop" have "Release 0.3" closed issue' do
project = Project.find_by_name("Shop")
Factory.create(:issue,
:title => "Release 0.3",
:project => project,
:author => project.users.first,
:closed => true)
end
end
class ProjectLabels < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should see label "bug"' do
within ".labels-table" do
page.should have_content "bug"
end
end
And 'I should see label "feature"' do
within ".labels-table" do
page.should have_content "feature"
end
end
And 'project "Shop" have issues tags: "bug", "feature"' do
project = Project.find_by_name("Shop")
['bug', 'feature'].each do |label|
Factory :issue, project: project, label_list: label
end
end
end
class ProjectMergeRequests < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
Then 'I should see "Bug NS-04" in merge requests' do
page.should have_content "Bug NS-04"
end
And 'I should not see "Feature NS-03" in merge requests' do
page.should_not have_content "Feature NS-03"
end
Given 'I click link "Closed"' do
click_link "Closed"
end
Then 'I should see "Feature NS-03" in merge requests' do
page.should have_content "Feature NS-03"
end
And 'I should not see "Bug NS-04" in merge requests' do
page.should_not have_content "Bug NS-04"
end
Given 'I click link "All"' do
click_link "All"
end
Given 'I click link "Bug NS-04"' do
click_link "Bug NS-04"
end
Then 'I should see merge request "Bug NS-04"' do
page.should have_content "Bug NS-04"
end
And 'I click link "Close"' do
click_link "Close"
end
Then 'I should see closed merge request "Bug NS-04"' do
mr = MergeRequest.find_by_title("Bug NS-04")
mr.closed.should be_true
page.should have_content "Closed by"
end
Given 'I click link "New Merge Request"' do
click_link "New Merge Request"
end
And 'I submit new merge request "Wiki Feature"' do
fill_in "merge_request_title", :with => "Wiki Feature"
select "master", :from => "merge_request_source_branch"
select "stable", :from => "merge_request_target_branch"
click_button "Save"
end
Then 'I should see merge request "Wiki Feature"' do
page.should have_content "Wiki Feature"
end
And 'project "Shop" have "Bug NS-04" open merge request' do
project = Project.find_by_name("Shop")
Factory.create(:merge_request,
:title => "Bug NS-04",
:project => project,
:author => project.users.first)
end
And 'project "Shop" have "Feature NS-03" closed merge request' do
project = Project.find_by_name("Shop")
Factory.create(:merge_request,
:title => "Feature NS-03",
:project => project,
:author => project.users.first,
:closed => true)
end
end
class ProjectMilestones < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should see milestone "v2.2"' do
milestone = @project.milestones.find_by_title("v2.2")
page.should have_content(milestone.title[0..10])
page.should have_content(milestone.expires_at)
page.should have_content("Browse Issues")
end
Given 'I click link "v2.2"' do
click_link "v2.2"
end
Given 'I click link "New Milestone"' do
click_link "New Milestone"
end
And 'I submit new milestone "v2.3"' do
fill_in "milestone_title", :with => "v2.3"
click_button "Create milestone"
end
Then 'I should see milestone "v2.3"' do
milestone = @project.milestones.find_by_title("v2.3")
page.should have_content(milestone.title[0..10])
page.should have_content(milestone.expires_at)
page.should have_content("Browse Issues")
end
And 'project "Shop" has milestone "v2.2"' do
project = Project.find_by_name("Shop")
milestone = Factory :milestone, :title => "v2.2", :project => project
3.times { Factory :issue, :project => project, :milestone => milestone }
end
end
class ProjectNetworkGraph < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
Then 'page should have network graph' do
page.should have_content "Project Network Graph"
within ".graph" do
page.should have_content "master"
page.should have_content "scss_refactor..."
end
end
And 'I visit project "Shop" network page' do
project = Project.find_by_name("Shop")
# Stub out find_all to speed this up (10 commits vs. 650)
commits = Grit::Commit.find_all(project.repo, nil, {max_count: 10})
Grit::Commit.stub(:find_all).and_return(commits)
visit graph_project_path(project)
end
end
class ProjectTeamManagement < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
Then 'I should be able to see myself in team' do
page.should have_content(@user.name)
page.should have_content(@user.email)
end
And 'I should see "Sam" in team list' do
user = User.find_by_name("Sam")
page.should have_content(user.name)
page.should have_content(user.email)
end
Given 'I click link "New Team Member"' do
click_link "New Team Member"
end
And 'I select "Mike" as "Reporter"' do
user = User.find_by_name("Mike")
within "#new_team_member" do
select user.name, :from => "user_ids"
select "Reporter", :from => "project_access"
end
click_button "Save"
end
Then 'I should see "Mike" in team list as "Reporter"' do
user = User.find_by_name("Mike")
role_id = find(".user_#{user.id} #team_member_project_access").value
role_id.should == UsersProject.access_roles["Reporter"].to_s
end
Given 'I should see "Sam" in team list as "Developer"' do
user = User.find_by_name("Sam")
role_id = find(".user_#{user.id} #team_member_project_access").value
role_id.should == UsersProject.access_roles["Developer"].to_s
end
And 'I change "Sam" role to "Reporter"' do
user = User.find_by_name("Sam")
within ".user_#{user.id}" do
select "Reporter", :from => "team_member_project_access"
end
end
And 'I should see "Sam" in team list as "Reporter"' do
user = User.find_by_name("Sam")
role_id = find(".user_#{user.id} #team_member_project_access").value
role_id.should == UsersProject.access_roles["Reporter"].to_s
end
Given 'I click link "Sam"' do
click_link "Sam"
end
Then 'I should see "Sam" team profile' do
user = User.find_by_name("Sam")
page.should have_content(user.name)
page.should have_content(user.email)
page.should have_content("To team list")
end
And 'I click link "Remove from team"' do
click_link "Remove from team"
end
And 'I should not see "Sam" in team list' do
user = User.find_by_name("Sam")
page.should_not have_content(user.name)
page.should_not have_content(user.email)
end
And 'gitlab user "Mike"' do
Factory :user, :name => "Mike"
end
And 'gitlab user "Sam"' do
Factory :user, :name => "Sam"
end
And '"Sam" is "Shop" developer' do
user = User.find_by_name("Sam")
project = Project.find_by_name("Shop")
project.add_access(user, :write)
end
end
class ProjectWall < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
end
class ProjectWiki < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedNote
include SharedPaths
Given 'I create Wiki page' do
fill_in "Title", :with => 'Test title'
fill_in "Content", :with => '[link test](test)'
click_on "Save"
end
Then 'I should see newly created wiki page' do
page.should have_content "Test title"
page.should have_content "link test"
click_link "link test"
page.should have_content "Editing page"
end
end
require Rails.root.join('spec', 'support', 'login_helpers')
module SharedAuthentication
include Spinach::DSL
include LoginHelpers
Given 'I sign in as a user' do
login_as :user
end
end
module SharedNote
include Spinach::DSL
Given 'I leave a comment like "XML attached"' do
fill_in "note_note", :with => "XML attached"
click_button "Add Comment"
end
Then 'I should see comment "XML attached"' do
page.should have_content "XML attached"
end
Given 'I write new comment "my special test message"' do
fill_in "note_note", :with => "my special test message"
click_button "Add Comment"
end
Then 'I should see project wall note "my special test message"' do
page.should have_content "my special test message"
end
end
module SharedPaths
include Spinach::DSL
And 'I visit dashboard search page' do
visit search_path
end
And 'I visit dashboard merge requests page' do
visit dashboard_merge_requests_path
end
And 'I visit dashboard issues page' do
visit dashboard_issues_path
end
When 'I visit dashboard page' do
visit dashboard_path
end
Given 'I visit profile page' do
visit profile_path
end
Given 'I visit profile account page' do
visit profile_account_path
end
Given 'I visit profile token page' do
visit profile_token_path
end
When 'I visit new project page' do
visit new_project_path
end
And 'I visit project "Shop" page' do
project = Project.find_by_name("Shop")
visit project_path(project)
end
Given 'I visit project branches page' do
visit branches_project_repository_path(@project)
end
Given 'I visit compare refs page' do
visit compare_project_commits_path(@project)
end
Given 'I visit project commits page' do
visit project_commits_path(@project)
end
Given 'I visit project source page' do
visit tree_project_ref_path(@project, @project.root_ref)
end
Given 'I visit blob file from repo' do
visit tree_project_ref_path(@project, ValidCommit::ID, :path => ValidCommit::BLOB_FILE_PATH)
end
Given 'I visit project source page for "8470d70"' do
visit tree_project_ref_path(@project, "8470d70")
end
Given 'I visit project tags page' do
visit tags_project_repository_path(@project)
end
Given 'I visit project commit page' do
visit project_commit_path(@project, ValidCommit::ID)
end
And 'I visit project "Shop" issues page' do
visit project_issues_path(Project.find_by_name("Shop"))
end
Given 'I visit issue page "Release 0.4"' do
issue = Issue.find_by_title("Release 0.4")
visit project_issue_path(issue.project, issue)
end
Given 'I visit project "Shop" labels page' do
visit project_labels_path(Project.find_by_name("Shop"))
end
Given 'I visit merge request page "Bug NS-04"' do
mr = MergeRequest.find_by_title("Bug NS-04")
visit project_merge_request_path(mr.project, mr)
end
And 'I visit project "Shop" merge requests page' do
visit project_merge_requests_path(Project.find_by_name("Shop"))
end
Given 'I visit project "Shop" milestones page' do
@project = Project.find_by_name("Shop")
visit project_milestones_path(@project)
end
Then 'I visit project "Shop" team page' do
visit project_team_index_path(Project.find_by_name("Shop"))
end
Then 'I visit project "Shop" wall page' do
project = Project.find_by_name("Shop")
visit wall_project_path(project)
end
Given 'I visit project wiki page' do
visit project_wiki_path(@project, :index)
end
end
module SharedProject
include Spinach::DSL
And 'I own project "Shop"' do
@project = Factory :project, :name => "Shop"
@project.add_access(@user, :admin)
end
end
unless ENV['CI'] ENV['RAILS_ENV'] = 'test'
require 'simplecov' require './config/environment'
SimpleCov.start 'rails'
end
require 'cucumber/rails'
require 'webmock/cucumber'
WebMock.allow_net_connect!
require Rails.root.join 'spec/support/gitolite_stub'
require Rails.root.join 'spec/support/stubbed_repository'
require Rails.root.join 'spec/support/login_helpers'
require Rails.root.join 'spec/support/valid_commit'
Capybara.default_selector = :css require 'rspec'
Capybara.javascript_driver = :webkit require 'database_cleaner'
require 'spinach/capybara'
# By default, any exception happening in your Rails application will bubble up %w(gitolite_stub stubbed_repository valid_commit).each do |f|
# to Cucumber so that your scenario will fail. This is a different from how require Rails.root.join('spec', 'support', f)
# your application behaves in the production environment, where an error page will
# be rendered instead.
#
# Sometimes we want to override this default behaviour and allow Rails to rescue
# exceptions and display an error page (just like when the app is running in production).
# Typical scenarios where you want to do this is when you test your error pages.
# There are two ways to allow Rails to rescue exceptions:
#
# 1) Tag your scenario (or feature) with @allow-rescue
#
# 2) Set the value below to true. Beware that doing this globally is not
# recommended as it will mask a lot of errors for you!
#
ActionController::Base.allow_rescue = false
# Remove/comment out the lines below if your app doesn't have a database.
# For some databases (like MongoDB and CouchDB) you may need to use :truncation instead.
begin
DatabaseCleaner.strategy = :transaction
rescue NameError
raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
end end
Cucumber::Rails::Database.javascript_strategy = :truncation Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file}
require 'headless' include GitoliteStub
headless = Headless.new WebMock.allow_net_connect!
headless.start Capybara.javascript_driver = :webkit
require 'cucumber/rspec/doubles' DatabaseCleaner.strategy = :truncation
Spinach.hooks.before_scenario { DatabaseCleaner.start }
Spinach.hooks.after_scenario { DatabaseCleaner.clean }
include GitoliteStub Spinach.hooks.before_run do
RSpec::Mocks::setup self
Before do
stub_gitolite! stub_gitolite!
end end
World(FactoryGirl::Syntax::Methods)
...@@ -17,5 +17,6 @@ module Gitlab ...@@ -17,5 +17,6 @@ module Gitlab
mount Projects mount Projects
mount Issues mount Issues
mount Milestones mount Milestones
mount Keys
end end
end end
...@@ -9,6 +9,10 @@ module Gitlab ...@@ -9,6 +9,10 @@ module Gitlab
expose :id, :email, :name, :blocked, :created_at expose :id, :email, :name, :blocked, :created_at
end end
class Hook < Grape::Entity
expose :id, :url
end
class Project < Grape::Entity class Project < Grape::Entity
expose :id, :code, :name, :description, :path, :default_branch expose :id, :code, :name, :description, :path, :default_branch
expose :owner, using: Entities::UserBasic expose :owner, using: Entities::UserBasic
...@@ -44,5 +48,11 @@ module Gitlab ...@@ -44,5 +48,11 @@ module Gitlab
expose :assignee, :author, using: Entities::UserBasic expose :assignee, :author, using: Entities::UserBasic
expose :closed, :updated_at, :created_at expose :closed, :updated_at, :created_at
end end
class Key < Grape::Entity
expose :id,
:title,
:key
end
end end
end end
...@@ -8,7 +8,7 @@ module Gitlab ...@@ -8,7 +8,7 @@ module Gitlab
if @project ||= current_user.projects.find_by_id(params[:id]) || if @project ||= current_user.projects.find_by_id(params[:id]) ||
current_user.projects.find_by_code(params[:id]) current_user.projects.find_by_code(params[:id])
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
@project @project
...@@ -19,15 +19,48 @@ module Gitlab ...@@ -19,15 +19,48 @@ module Gitlab
end end
def authenticate! def authenticate!
error!({'message' => '401 Unauthorized'}, 401) unless current_user unauthorized! unless current_user
end end
def authorize! action, subject def authorize! action, subject
unless abilities.allowed?(current_user, action, subject) unless abilities.allowed?(current_user, action, subject)
error!({'message' => '403 Forbidden'}, 403) forbidden!
end end
end end
def attributes_for_keys(keys)
attrs = {}
keys.each do |key|
attrs[key] = params[key] if params[key].present?
end
attrs
end
# error helpers
def forbidden!
render_api_error!('403 Forbidden', 403)
end
def not_found!(resource = nil)
message = ["404"]
message << resource if resource
message << "Not Found"
render_api_error!(message.join(' '), 404)
end
def unauthorized!
render_api_error!('401 Unauthorized', 401)
end
def not_allowed!
render_api_error!('Method Not Allowed', 405)
end
def render_api_error!(message, status)
error!({'message' => message}, status)
end
private private
def abilities def abilities
......
...@@ -48,19 +48,14 @@ module Gitlab ...@@ -48,19 +48,14 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/issues # POST /projects/:id/issues
post ":id/issues" do post ":id/issues" do
@issue = user_project.issues.new( attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
title: params[:title], attrs[:label_list] = params[:labels] if params[:labels].present?
description: params[:description], @issue = user_project.issues.new attrs
assignee_id: params[:assignee_id],
milestone_id: params[:milestone_id],
label_list: params[:labels]
)
@issue.author = current_user @issue.author = current_user
if @issue.save if @issue.save
present @issue, with: Entities::Issue present @issue, with: Entities::Issue
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
...@@ -81,19 +76,12 @@ module Gitlab ...@@ -81,19 +76,12 @@ module Gitlab
@issue = user_project.issues.find(params[:issue_id]) @issue = user_project.issues.find(params[:issue_id])
authorize! :modify_issue, @issue authorize! :modify_issue, @issue
parameters = { attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :closed]
title: (params[:title] || @issue.title), attrs[:label_list] = params[:labels] if params[:labels].present?
description: (params[:description] || @issue.description), if @issue.update_attributes attrs
assignee_id: (params[:assignee_id] || @issue.assignee_id),
milestone_id: (params[:milestone_id] || @issue.milestone_id),
label_list: (params[:labels] || @issue.label_list),
closed: (params[:closed] || @issue.closed)
}
if @issue.update_attributes(parameters)
present @issue, with: Entities::Issue present @issue, with: Entities::Issue
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
...@@ -105,7 +93,7 @@ module Gitlab ...@@ -105,7 +93,7 @@ module Gitlab
# Example Request: # Example Request:
# DELETE /projects/:id/issues/:issue_id # DELETE /projects/:id/issues/:issue_id
delete ":id/issues/:issue_id" do delete ":id/issues/:issue_id" do
error!({'message' => 'method not allowed'}, 405) not_allowed!
end end
end end
end end
......
module Gitlab
# Keys API
class Keys < Grape::API
before { authenticate! }
resource :keys do
# Get currently authenticated user's keys
#
# Example Request:
# GET /keys
get do
present current_user.keys, with: Entities::Key
end
# Get single key owned by currently authenticated user
#
# Example Request:
# GET /keys/:id
get "/:id" do
key = current_user.keys.find params[:id]
present key, with: Entities::Key
end
# Add new ssh key to currently authenticated user
#
# Parameters:
# key (required) - New SSH Key
# title (required) - New SSH Key's title
# Example Request:
# POST /keys
post do
attrs = attributes_for_keys [:title, :key]
key = current_user.keys.new attrs
if key.save
present key, with: Entities::Key
else
not_found!
end
end
# Delete existed ssh key of currently authenticated user
#
# Parameters:
# id (required) - SSH Key ID
# Example Request:
# DELETE /keys/:id
delete "/:id" do
key = current_user.keys.find params[:id]
key.delete
end
end
end
end
...@@ -36,16 +36,12 @@ module Gitlab ...@@ -36,16 +36,12 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/milestones # POST /projects/:id/milestones
post ":id/milestones" do post ":id/milestones" do
@milestone = user_project.milestones.new( attrs = attributes_for_keys [:title, :description, :due_date]
title: params[:title], @milestone = user_project.milestones.new attrs
description: params[:description],
due_date: params[:due_date]
)
if @milestone.save if @milestone.save
present @milestone, with: Entities::Milestone present @milestone, with: Entities::Milestone
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
...@@ -64,17 +60,11 @@ module Gitlab ...@@ -64,17 +60,11 @@ module Gitlab
authorize! :admin_milestone, user_project authorize! :admin_milestone, user_project
@milestone = user_project.milestones.find(params[:milestone_id]) @milestone = user_project.milestones.find(params[:milestone_id])
parameters = { attrs = attributes_for_keys [:title, :description, :due_date, :closed]
title: (params[:title] || @milestone.title), if @milestone.update_attributes attrs
description: (params[:description] || @milestone.description),
due_date: (params[:due_date] || @milestone.due_date),
closed: (params[:closed] || @milestone.closed)
}
if @milestone.update_attributes(parameters)
present @milestone, with: Entities::Milestone present @milestone, with: Entities::Milestone
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
end end
......
...@@ -40,17 +40,20 @@ module Gitlab ...@@ -40,17 +40,20 @@ module Gitlab
post do post do
params[:code] ||= params[:name] params[:code] ||= params[:name]
params[:path] ||= params[:name] params[:path] ||= params[:name]
project_attrs = {} attrs = attributes_for_keys [:code,
params.each_pair do |k ,v| :path,
if Project.attribute_names.include? k :name,
project_attrs[k] = v :description,
end :default_branch,
end :issues_enabled,
@project = Project.create_by_user(project_attrs, current_user) :wall_enabled,
:merge_requests_enabled,
:wiki_enabled]
@project = Project.create_by_user(attrs, current_user)
if @project.saved? if @project.saved?
present @project, with: Entities::Project present @project, with: Entities::Project
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
...@@ -106,6 +109,49 @@ module Gitlab ...@@ -106,6 +109,49 @@ module Gitlab
nil nil
end end
# Get project hooks
#
# Parameters:
# id (required) - The ID or code name of a project
# Example Request:
# GET /projects/:id/hooks
get ":id/hooks" do
authorize! :admin_project, user_project
@hooks = paginate user_project.hooks
present @hooks, with: Entities::Hook
end
# Add hook to project
#
# Parameters:
# id (required) - The ID or code name of a project
# url (required) - The hook URL
# Example Request:
# POST /projects/:id/hooks
post ":id/hooks" do
authorize! :admin_project, user_project
@hook = user_project.hooks.new({"url" => params[:url]})
if @hook.save
present @hook, with: Entities::Hook
else
error!({'message' => '404 Not found'}, 404)
end
end
# Delete project hook
#
# Parameters:
# id (required) - The ID or code name of a project
# hook_id (required) - The ID of hook to delete
# Example Request:
# DELETE /projects/:id/hooks
delete ":id/hooks" do
authorize! :admin_project, user_project
@hook = user_project.hooks.find(params[:hook_id])
@hook.destroy
nil
end
# Get a project repository branches # Get a project repository branches
# #
# Parameters: # Parameters:
...@@ -161,18 +207,16 @@ module Gitlab ...@@ -161,18 +207,16 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/snippets # POST /projects/:id/snippets
post ":id/snippets" do post ":id/snippets" do
@snippet = user_project.snippets.new( attrs = attributes_for_keys [:title, :file_name]
title: params[:title], attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
file_name: params[:file_name], attrs[:content] = params[:code] if params[:code].present?
expires_at: params[:lifetime], @snippet = user_project.snippets.new attrs
content: params[:code]
)
@snippet.author = current_user @snippet.author = current_user
if @snippet.save if @snippet.save
present @snippet, with: Entities::ProjectSnippet present @snippet, with: Entities::ProjectSnippet
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
...@@ -191,17 +235,14 @@ module Gitlab ...@@ -191,17 +235,14 @@ module Gitlab
@snippet = user_project.snippets.find(params[:snippet_id]) @snippet = user_project.snippets.find(params[:snippet_id])
authorize! :modify_snippet, @snippet authorize! :modify_snippet, @snippet
parameters = { attrs = attributes_for_keys [:title, :file_name]
title: (params[:title] || @snippet.title), attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
file_name: (params[:file_name] || @snippet.file_name), attrs[:content] = params[:code] if params[:code].present?
expires_at: (params[:lifetime] || @snippet.expires_at),
content: (params[:code] || @snippet.content)
}
if @snippet.update_attributes(parameters) if @snippet.update_attributes attrs
present @snippet, with: Entities::ProjectSnippet present @snippet, with: Entities::ProjectSnippet
else else
error!({'message' => '404 Not found'}, 404) not_found!
end end
end end
...@@ -244,10 +285,10 @@ module Gitlab ...@@ -244,10 +285,10 @@ module Gitlab
ref = params[:sha] ref = params[:sha]
commit = user_project.commit ref commit = user_project.commit ref
error!('404 Commit Not Found', 404) unless commit not_found! "Commit" unless commit
tree = Tree.new commit.tree, user_project, ref, params[:filepath] tree = Tree.new commit.tree, user_project, ref, params[:filepath]
error!('404 File Not Found', 404) unless tree.try(:tree) not_found! "File" unless tree.try(:tree)
if tree.text? if tree.text?
encoding = Gitlab::Encode.detect_encoding(tree.data) encoding = Gitlab::Encode.detect_encoding(tree.data)
......
module Gitlab
class AppLogger < Gitlab::Logger
def self.file_name
'application.log'
end
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_s(:long)}: #{msg}\n"
end
end
end
module Gitlab
class Auth
def find_for_ldap_auth(auth, signed_in_resource = nil)
uid = auth.info.uid
provider = auth.provider
email = auth.info.email.downcase unless auth.info.email.nil?
raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil?
if @user = User.find_by_extern_uid_and_provider(uid, provider)
@user
elsif @user = User.find_by_email(email)
log.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"
@user.update_attributes(:extern_uid => uid, :provider => provider)
@user
else
create_from_omniauth(auth, true)
end
end
def create_from_omniauth(auth, ldap = false)
provider = auth.provider
uid = auth.info.uid || auth.uid
name = auth.info.name.force_encoding("utf-8")
email = auth.info.email.downcase unless auth.info.email.nil?
ldap_prefix = ldap ? '(LDAP) ' : ''
raise OmniAuth::Error, "#{ldap_prefix}#{provider} does not provide an email"\
" address" if auth.info.email.blank?
log.info "#{ldap_prefix}Creating user from #{provider} login"\
" {uid => #{uid}, name => #{name}, email => #{email}}"
password = Devise.friendly_token[0, 8].downcase
@user = User.new(
extern_uid: uid,
provider: provider,
name: name,
email: email,
password: password,
password_confirmation: password,
projects_limit: Gitlab.config.default_projects_limit,
)
if Gitlab.config.omniauth['block_auto_created_users'] && !ldap
@user.blocked = true
end
@user.save!
@user
end
def find_or_new_for_omniauth(auth)
provider, uid = auth.provider, auth.uid
if @user = User.find_by_provider_and_extern_uid(provider, uid)
@user
else
if Gitlab.config.omniauth['allow_single_sign_on']
@user = create_from_omniauth(auth)
@user
end
end
end
def log
Gitlab::AppLogger
end
end
end
...@@ -58,18 +58,22 @@ module Gitlab ...@@ -58,18 +58,22 @@ module Gitlab
end end
end end
rescue PullError => ex rescue PullError => ex
Gitlab::Logger.error("Pull error -> " + ex.message) log("Pull error -> " + ex.message)
raise Gitolite::AccessDenied, ex.message raise Gitolite::AccessDenied, ex.message
rescue PushError => ex rescue PushError => ex
Gitlab::Logger.error("Push error -> " + " " + ex.message) log("Push error -> " + " " + ex.message)
raise Gitolite::AccessDenied, ex.message raise Gitolite::AccessDenied, ex.message
rescue Exception => ex rescue Exception => ex
Gitlab::Logger.error(ex.class.name + " " + ex.message) log(ex.class.name + " " + ex.message)
raise Gitolite::AccessDenied.new("gitolite timeout") raise Gitolite::AccessDenied.new("gitolite timeout")
end end
def log message
Gitlab::GitLogger.error(message)
end
def destroy_project(project) def destroy_project(project)
FileUtils.rm_rf(project.path_to_repo) FileUtils.rm_rf(project.path_to_repo)
conf.rm_repo(project.path) conf.rm_repo(project.path)
...@@ -148,7 +152,7 @@ module Gitlab ...@@ -148,7 +152,7 @@ module Gitlab
# Enable access to all repos for gitolite admin. # Enable access to all repos for gitolite admin.
# We use it for accept merge request feature # We use it for accept merge request feature
def admin_all_repo def admin_all_repo
owner_name = Gitlab.settings.gitolite_admin_key owner_name = Gitlab.config.gitolite_admin_key
# @ALL repos premission for gitolite owner # @ALL repos premission for gitolite owner
repo_name = "@all" repo_name = "@all"
......
...@@ -12,21 +12,22 @@ module Grack ...@@ -12,21 +12,22 @@ module Grack
# Pass Gitolite update hook # Pass Gitolite update hook
ENV['GL_BYPASS_UPDATE_HOOK'] = "true" ENV['GL_BYPASS_UPDATE_HOOK'] = "true"
# Need this patch because the rails mount # Need this patch due to the rails mount
@env['PATH_INFO'] = @env['REQUEST_PATH'] @env['PATH_INFO'] = @request.path
@env['SCRIPT_NAME'] = ""
# Find project by PATH_INFO from env # Find project by PATH_INFO from env
if m = /^\/([\w-]+).git/.match(@env['PATH_INFO']).to_a if m = /^\/([\w-]+).git/.match(@request.path_info).to_a
return false unless project = Project.find_by_path(m.last) return false unless project = Project.find_by_path(m.last)
end end
# Git upload and receive # Git upload and receive
if @env['REQUEST_METHOD'] == 'GET' if @request.get?
true true
elsif @env['REQUEST_METHOD'] == 'POST' elsif @request.post?
if @env['REQUEST_URI'].end_with?('git-upload-pack') if @request.path_info.end_with?('git-upload-pack')
return project.dev_access_for?(user) return project.dev_access_for?(user)
elsif @env['REQUEST_URI'].end_with?('git-receive-pack') elsif @request.path_info.end_with?('git-receive-pack')
if project.protected_branches.map(&:name).include?(current_ref) if project.protected_branches.map(&:name).include?(current_ref)
project.master_access_for?(user) project.master_access_for?(user)
else else
......
module Gitlab
class GitLogger < Gitlab::Logger
def self.file_name
'githost.log'
end
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_s(:long)} -> #{severity} -> #{msg}\n"
end
end
end
...@@ -5,6 +5,8 @@ module Gitlab ...@@ -5,6 +5,8 @@ module Gitlab
attr_accessor :time, :space attr_accessor :time, :space
attr_accessor :refs attr_accessor :refs
include ActionView::Helpers::SanitizeHelper
def self.to_graph(project) def self.to_graph(project)
@repo = project.repo @repo = project.repo
commits = Grit::Commit.find_all(@repo, nil, {max_count: 650}) commits = Grit::Commit.find_all(@repo, nil, {max_count: 650})
...@@ -164,7 +166,7 @@ module Gitlab ...@@ -164,7 +166,7 @@ module Gitlab
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil? h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha h[:id] = sha
h[:date] = date h[:date] = date
h[:message] = Gitlab::Encode.utf8(message) h[:message] = sanitize(Gitlab::Encode.utf8(message))
h[:login] = author.email h[:login] = author.email
h h
end end
......
...@@ -9,17 +9,13 @@ module Gitlab ...@@ -9,17 +9,13 @@ module Gitlab
end end
def self.read_latest def self.read_latest
path = Rails.root.join("log/githost.log") path = Rails.root.join("log", file_name)
self.build unless File.exist?(path) self.build unless File.exist?(path)
logs = File.read(path).split("\n") logs = File.read(path).split("\n")
end end
def self.build def self.build
new(File.join(Rails.root, "log/githost.log")) new(File.join(Rails.root, "log", file_name))
end
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_s(:long)} -> #{severity} -> #{msg}\n"
end end
end end
end end
...@@ -26,13 +26,13 @@ module Gitlab ...@@ -26,13 +26,13 @@ module Gitlab
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" /> # => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
module Markdown module Markdown
REFERENCE_PATTERN = %r{ REFERENCE_PATTERN = %r{
([^\w&;])? # Prefix (1) (\W)? # Prefix (1)
( # Reference (2) ( # Reference (2)
@([\w\._]+) # User name (3) @([\w\._]+) # User name (3)
|[#!$](\d+) # Issue/MR/Snippet ID (4) |[#!$](\d+) # Issue/MR/Snippet ID (4)
|([\h]{6,40}) # Commit ID (5) |([\h]{6,40}) # Commit ID (5)
) )
([^\w&;])? # Suffix (6) (\W)? # Suffix (6)
}x.freeze }x.freeze
EMOJI_PATTERN = %r{(:(\S+):)}.freeze EMOJI_PATTERN = %r{(:(\S+):)}.freeze
...@@ -48,8 +48,10 @@ module Gitlab ...@@ -48,8 +48,10 @@ module Gitlab
def gfm(text, html_options = {}) def gfm(text, html_options = {})
return text if text.nil? return text if text.nil?
# prevents the string supplied through the _text_ argument to be altered # Duplicate the string so we don't alter the original, then call to_str
text = text.dup # to cast it back to a String instead of a SafeBuffer. This is required
# for gsub calls to work as we need them to.
text = text.dup.to_str
@html_options = html_options @html_options = html_options
...@@ -84,6 +86,13 @@ module Gitlab ...@@ -84,6 +86,13 @@ module Gitlab
# #
# Returns parsed text # Returns parsed text
def parse(text) def parse(text)
parse_references(text) if @project
parse_emoji(text)
text
end
def parse_references(text)
# parse reference links # parse reference links
text.gsub!(REFERENCE_PATTERN) do |match| text.gsub!(REFERENCE_PATTERN) do |match|
prefix = $1 || '' prefix = $1 || ''
...@@ -91,13 +100,18 @@ module Gitlab ...@@ -91,13 +100,18 @@ module Gitlab
identifier = $3 || $4 || $5 identifier = $3 || $4 || $5
suffix = $6 || '' suffix = $6 || ''
if ref_link = reference_link(reference, identifier) # Avoid HTML entities
if prefix.ends_with?('&') || suffix.starts_with?(';')
match
elsif ref_link = reference_link(reference, identifier)
prefix + ref_link + suffix prefix + ref_link + suffix
else else
match match
end end
end if @project end
end
def parse_emoji(text)
# parse emoji # parse emoji
text.gsub!(EMOJI_PATTERN) do |match| text.gsub!(EMOJI_PATTERN) do |match|
if valid_emoji?($2) if valid_emoji?($2)
...@@ -106,8 +120,6 @@ module Gitlab ...@@ -106,8 +120,6 @@ module Gitlab
match match
end end
end end
text
end end
# Private: Checks if an emoji icon exists in the image asset directory # Private: Checks if an emoji icon exists in the image asset directory
......
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
# It is recommended to regenerate this file in the future when you upgrade to a
# newer version of cucumber-rails. Consider adding your own code to a new file
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
# files.
unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
begin
require 'cucumber/rake/task'
namespace :cucumber do
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
t.fork = true # You may get faster startup if you set this to false
t.profile = 'default'
end
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
t.binary = vendored_cucumber_bin
t.fork = true # You may get faster startup if you set this to false
t.profile = 'wip'
end
Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
t.binary = vendored_cucumber_bin
t.fork = true # You may get faster startup if you set this to false
t.profile = 'rerun'
end
desc 'Run all features'
task :all => [:ok, :wip]
task :statsetup do
require 'rails/code_statistics'
::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features')
::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features')
end
end
desc 'Alias for cucumber:ok'
task :cucumber => 'cucumber:ok'
task :default => :cucumber
task :features => :cucumber do
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
end
# In case we don't have ActiveRecord, append a no-op task that we can depend upon.
task 'db:test:prepare' do
end
task :stats => 'cucumber:statsetup'
rescue LoadError
desc 'cucumber rake task not available (cucumber not installed)'
task :cucumber do
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
end
end
end
namespace :gitlab do namespace :gitlab do
desc "GITLAB | Run both cucumber & rspec" desc "GITLAB | Run both spinach and rspec"
task :test => ['cucumber', 'spec'] task :test => ['spinach', 'spec']
end end
task :travis do task :travis do
["cucumber", "rspec spec"].each do |cmd| ["spinach", "rspec spec"].each do |cmd|
puts "Starting to run #{cmd}..." puts "Starting to run #{cmd}..."
system("export DISPLAY=:99.0 && bundle exec #{cmd}") system("export DISPLAY=:99.0 && bundle exec #{cmd}")
raise "#{cmd} failed!" unless $?.exitstatus == 0 raise "#{cmd} failed!" unless $?.exitstatus == 0
......
#!/usr/bin/env ruby
vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
if vendored_cucumber_bin
load File.expand_path(vendored_cucumber_bin)
else
require 'rubygems' unless ENV['NO_RUBYGEMS']
require 'cucumber'
load Cucumber::BINARY
end
...@@ -31,6 +31,7 @@ describe GitlabMarkdownHelper do ...@@ -31,6 +31,7 @@ describe GitlabMarkdownHelper do
end end
it "should not touch HTML entities" do it "should not touch HTML entities" do
@project.issues.stub(:where).with(id: '39').and_return([issue])
actual = expected = "We&#39;ll accept good pull requests." actual = expected = "We&#39;ll accept good pull requests."
gfm(actual).should == expected gfm(actual).should == expected
end end
...@@ -291,11 +292,18 @@ describe GitlabMarkdownHelper do ...@@ -291,11 +292,18 @@ describe GitlabMarkdownHelper do
actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo') actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
actual.should have_selector 'a.gfm.gfm-commit.foo' actual.should have_selector 'a.gfm.gfm-commit.foo'
end end
it "escapes HTML passed in as the body" do
actual = "This is a <h1>test</h1> - see ##{issues[0].id}"
link_to_gfm(actual, commit_path).should match('&lt;h1&gt;test&lt;/h1&gt;')
end
end end
describe "#markdown" do describe "#markdown" do
it "should handle references in paragraphs" do it "should handle references in paragraphs" do
markdown("\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. #{commit.id} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.\n").should == "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. #{link_to commit.id, project_commit_path(project, commit), title: commit.link_title, class: "gfm gfm-commit "} Nam pulvinar sapien eget odio adipiscing at faucibus orci vestibulum.</p>\n" actual = "\n\nLorem ipsum dolor sit amet. #{commit.id} Nam pulvinar sapien eget.\n"
expected = project_commit_path(project, commit)
markdown(actual).should match(expected)
end end
it "should handle references in headers" do it "should handle references in headers" do
......
require 'spec_helper'
describe Gitlab::Auth do
let(:gl_auth) { Gitlab::Auth.new }
before do
Gitlab.config.stub(omniauth: {})
@info = mock(
uid: '12djsak321',
name: 'John',
email: 'john@mail.com'
)
end
describe :find_for_ldap_auth do
before do
@auth = mock(
uid: '12djsak321',
info: @info,
provider: 'ldap'
)
end
it "should find by uid & provider" do
User.should_receive :find_by_extern_uid_and_provider
gl_auth.find_for_ldap_auth(@auth)
end
it "should update credentials by email if missing uid" do
user = double('User')
User.stub find_by_extern_uid_and_provider: nil
User.stub find_by_email: user
user.should_receive :update_attributes
gl_auth.find_for_ldap_auth(@auth)
end
it "should create from auth if user doesnot exist"do
User.stub find_by_extern_uid_and_provider: nil
User.stub find_by_email: nil
gl_auth.should_receive :create_from_omniauth
gl_auth.find_for_ldap_auth(@auth)
end
end
describe :find_or_new_for_omniauth do
before do
@auth = mock(
info: @info,
provider: 'twitter',
uid: '12djsak321',
)
end
it "should find user"do
User.should_receive :find_by_provider_and_extern_uid
gl_auth.should_not_receive :create_from_omniauth
gl_auth.find_or_new_for_omniauth(@auth)
end
it "should not create user"do
User.stub find_by_provider_and_extern_uid: nil
gl_auth.should_not_receive :create_from_omniauth
gl_auth.find_or_new_for_omniauth(@auth)
end
it "should create user if single_sing_on"do
Gitlab.config.omniauth['allow_single_sign_on'] = true
User.stub find_by_provider_and_extern_uid: nil
gl_auth.should_receive :create_from_omniauth
gl_auth.find_or_new_for_omniauth(@auth)
end
end
describe :create_from_omniauth do
it "should create user from LDAP" do
@auth = mock(info: @info, provider: 'ldap')
user = gl_auth.create_from_omniauth(@auth, true)
user.should be_valid
user.extern_uid.should == @info.uid
user.provider.should == 'ldap'
end
it "should create user from Omniauth" do
@auth = mock(info: @info, provider: 'twitter')
user = gl_auth.create_from_omniauth(@auth, false)
user.should be_valid
user.extern_uid.should == @info.uid
user.provider.should == 'twitter'
end
end
end
...@@ -50,25 +50,24 @@ describe Event do ...@@ -50,25 +50,24 @@ describe Event do
it { @event.author.should == @user } it { @event.author.should == @user }
end end
describe 'Team events' do
let(:user_project) { stub.as_null_object }
let(:observer) { UsersProjectObserver.instance }
before {
Event.should_receive :create
}
describe "Joined project team" do describe "Joined project team" do
let(:project) {Factory.create :project}
let(:new_user) {Factory.create :user}
it "should create event" do it "should create event" do
UsersProject.observers.enable :users_project_observer observer.after_create user_project
expect{
UsersProject.bulk_import(project, [new_user.id], UsersProject::DEVELOPER)
}.to change{Event.count}.by(1)
end end
end end
describe "Left project team" do describe "Left project team" do
let(:project) {Factory.create :project}
let(:new_user) {Factory.create :user}
it "should create event" do it "should create event" do
UsersProject.bulk_import(project, [new_user.id], UsersProject::DEVELOPER) observer.after_destroy user_project
UsersProject.observers.enable :users_project_observer end
expect{
UsersProject.bulk_delete(project, [new_user.id])
}.to change{Event.count}.by(1)
end end
end end
end end
...@@ -12,7 +12,7 @@ describe Issue do ...@@ -12,7 +12,7 @@ describe Issue do
describe 'modules' do describe 'modules' do
it { should include_module(IssueCommonality) } it { should include_module(IssueCommonality) }
it { should include_module(Upvote) } it { should include_module(Votes) }
end end
subject { Factory.create(:issue) } subject { Factory.create(:issue) }
......
...@@ -8,6 +8,6 @@ describe MergeRequest do ...@@ -8,6 +8,6 @@ describe MergeRequest do
describe 'modules' do describe 'modules' do
it { should include_module(IssueCommonality) } it { should include_module(IssueCommonality) }
it { should include_module(Upvote) } it { should include_module(Votes) }
end end
end end
...@@ -24,6 +24,13 @@ describe Note do ...@@ -24,6 +24,13 @@ describe Note do
it "recognizes a neutral note" do it "recognizes a neutral note" do
note = Factory(:note, note: "This is not a +1 note") note = Factory(:note, note: "This is not a +1 note")
note.should_not be_upvote note.should_not be_upvote
note.should_not be_downvote
end
it "recognizes a neutral emoji note" do
note = build(:note, note: "I would :+1: this, but I don't want to")
note.should_not be_upvote
note.should_not be_downvote
end end
it "recognizes a +1 note" do it "recognizes a +1 note" do
...@@ -31,19 +38,19 @@ describe Note do ...@@ -31,19 +38,19 @@ describe Note do
note.should be_upvote note.should be_upvote
end end
it "recognizes a -1 note as no vote" do
note = Factory(:note, note: "-1 for this")
note.should_not be_upvote
end
it "recognizes a +1 emoji as a vote" do it "recognizes a +1 emoji as a vote" do
note = build(:note, note: ":+1: for this") note = build(:note, note: ":+1: for this")
note.should be_upvote note.should be_upvote
end end
it "recognizes a neutral emoji note" do it "recognizes a -1 note" do
note = build(:note, note: "I would :+1: this, but I don't want to") note = Factory(:note, note: "-1 for this")
note.should_not be_upvote note.should be_downvote
end
it "recognizes a -1 emoji as a vote" do
note = build(:note, note: ":-1: for this")
note.should be_downvote
end end
end end
......
...@@ -13,7 +13,7 @@ describe UserObserver do ...@@ -13,7 +13,7 @@ describe UserObserver do
end end
context 'when a new user is created' do context 'when a new user is created' do
let(:user) { double(:user, id: 42, password: 'P@ssword!') } let(:user) { double(:user, id: 42, password: 'P@ssword!', name: 'John', email: 'u@mail.local') }
let(:notification) { double :notification } let(:notification) { double :notification }
it 'sends an email' do it 'sends an email' do
...@@ -22,5 +22,10 @@ describe UserObserver do ...@@ -22,5 +22,10 @@ describe UserObserver do
subject.after_create(user) subject.after_create(user)
end end
it 'trigger logger' do
Gitlab::AppLogger.should_receive(:info)
subject.after_create(user)
end
end end
end end
require 'spec_helper' require 'spec_helper'
describe UsersProjectObserver do describe UsersProjectObserver do
# let(:users_project) { stub.as_null_object }
let(:user) { Factory.create :user } let(:user) { Factory.create :user }
let(:project) { Factory.create(:project, let(:project) { Factory.create(:project,
code: "Fuu", code: "Fuu",
...@@ -14,21 +15,23 @@ describe UsersProjectObserver do ...@@ -14,21 +15,23 @@ describe UsersProjectObserver do
it "should called when UsersProject created" do it "should called when UsersProject created" do
subject.should_receive(:after_commit).once subject.should_receive(:after_commit).once
UsersProject.observers.enable :users_project_observer do UsersProject.observers.enable :users_project_observer do
Factory.create(:users_project, create(:users_project)
project: project,
user: user)
end end
end end
it "should send email to user" do it "should send email to user" do
Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true)) Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true))
subject.after_commit(users_project) subject.after_commit(users_project)
Event.stub(:create => true)
end end
it "should create new event" do it "should create new event" do
Event.should_receive(:create).with( Event.should_receive(:create).with(
project_id: users_project.project.id, project_id: users_project.project.id,
action: Event::Joined, action: Event::Joined,
author_id: users_project.user.id author_id: users_project.user.id
) )
subject.after_create(users_project) subject.after_create(users_project)
end end
end end
...@@ -37,9 +40,10 @@ describe UsersProjectObserver do ...@@ -37,9 +40,10 @@ describe UsersProjectObserver do
it "should called when UsersProject updated" do it "should called when UsersProject updated" do
subject.should_receive(:after_commit).once subject.should_receive(:after_commit).once
UsersProject.observers.enable :users_project_observer do UsersProject.observers.enable :users_project_observer do
users_project.update_attribute(:project_access, 40) create(:users_project).update_attribute(:project_access, UsersProject::MASTER)
end end
end end
it "should send email to user" do it "should send email to user" do
Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true)) Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true))
subject.after_commit(users_project) subject.after_commit(users_project)
...@@ -51,16 +55,16 @@ describe UsersProjectObserver do ...@@ -51,16 +55,16 @@ describe UsersProjectObserver do
end end
end end
end end
describe "#after_destroy" do describe "#after_destroy" do
it "should called when UsersProject destroyed" do it "should called when UsersProject destroyed" do
subject.should_receive(:after_destroy) subject.should_receive(:after_destroy)
UsersProject.observers.enable :users_project_observer do UsersProject.observers.enable :users_project_observer do
UsersProject.bulk_delete( create(:users_project).destroy
users_project.project,
[users_project.user.id]
)
end end
end end
it "should create new event" do it "should create new event" do
Event.should_receive(:create).with( Event.should_receive(:create).with(
project_id: users_project.project.id, project_id: users_project.project.id,
......
...@@ -9,12 +9,14 @@ describe Gitlab::API do ...@@ -9,12 +9,14 @@ describe Gitlab::API do
before { project.add_access(user, :read) } before { project.add_access(user, :read) }
describe "GET /issues" do describe "GET /issues" do
context "when unauthenticated" do
it "should return authentication error" do it "should return authentication error" do
get api("/issues") get api("/issues")
response.status.should == 401 response.status.should == 401
end end
end
describe "authenticated GET /issues" do context "when authenticated" do
it "should return an array of issues" do it "should return an array of issues" do
get api("/issues", user) get api("/issues", user)
response.status.should == 200 response.status.should == 200
......
...@@ -6,6 +6,7 @@ describe Gitlab::API do ...@@ -6,6 +6,7 @@ describe Gitlab::API do
let(:user) { Factory :user } let(:user) { Factory :user }
let(:user2) { Factory.create(:user) } let(:user2) { Factory.create(:user) }
let(:user3) { Factory.create(:user) } let(:user3) { Factory.create(:user) }
let!(:hook) { Factory :project_hook, project: project, url: "http://example.com" }
let!(:project) { Factory :project, owner: user } let!(:project) { Factory :project, owner: user }
let!(:snippet) { Factory :snippet, author: user, project: project, title: 'example' } let!(:snippet) { Factory :snippet, author: user, project: project, title: 'example' }
let!(:users_project) { Factory :users_project, user: user, project: project, project_access: UsersProject::MASTER } let!(:users_project) { Factory :users_project, user: user, project: project, project_access: UsersProject::MASTER }
...@@ -13,12 +14,14 @@ describe Gitlab::API do ...@@ -13,12 +14,14 @@ describe Gitlab::API do
before { project.add_access(user, :read) } before { project.add_access(user, :read) }
describe "GET /projects" do describe "GET /projects" do
context "when unauthenticated" do
it "should return authentication error" do it "should return authentication error" do
get api("/projects") get api("/projects")
response.status.should == 401 response.status.should == 401
end end
end
describe "authenticated GET /projects" do context "when authenticated" do
it "should return an array of projects" do it "should return an array of projects" do
get api("/projects", user) get api("/projects", user)
response.status.should == 200 response.status.should == 200
...@@ -85,7 +88,7 @@ describe Gitlab::API do ...@@ -85,7 +88,7 @@ describe Gitlab::API do
it "should return a 404 error if not found" do it "should return a 404 error if not found" do
get api("/projects/42", user) get api("/projects/42", user)
response.status.should == 404 response.status.should == 404
json_response['message'].should == '404 Not found' json_response['message'].should == '404 Not Found'
end end
end end
...@@ -147,6 +150,36 @@ describe Gitlab::API do ...@@ -147,6 +150,36 @@ describe Gitlab::API do
end end
end end
describe "GET /projects/:id/hooks" do
it "should return project hooks" do
get api("/projects/#{project.code}/hooks", user)
response.status.should == 200
json_response.should be_an Array
json_response.count.should == 1
json_response.first['url'].should == "http://example.com"
end
end
describe "POST /projects/:id/users" do
it "should add hook to project" do
expect {
post api("/projects/#{project.code}/hooks", user),
"url" => "http://example.com"
}.to change {project.hooks.count}.by(1)
end
end
describe "DELETE /projects/:id/hooks" do
it "should delete hook from project" do
expect {
delete api("/projects/#{project.code}/hooks", user),
hook_id: hook.id
}.to change {project.hooks.count}.by(-1)
end
end
describe "GET /projects/:id/repository/tags" do describe "GET /projects/:id/repository/tags" do
it "should return an array of project tags" do it "should return an array of project tags" do
get api("/projects/#{project.code}/repository/tags", user) get api("/projects/#{project.code}/repository/tags", user)
......
require 'spec_helper'
describe Gitlab::Keys do
include ApiHelpers
let(:user) {
user = Factory.create :user
user.reset_authentication_token!
user
}
let(:key) { Factory.create :key, { user: user}}
describe "GET /keys" do
context "when unauthenticated" do
it "should return authentication error" do
get api("/keys")
response.status.should == 401
end
end
context "when authenticated" do
it "should return array of ssh keys" do
user.keys << key
user.save
get api("/keys", user)
response.status.should == 200
json_response.should be_an Array
json_response.first["title"].should == key.title
end
end
end
describe "GET /keys/:id" do
it "should returm single key" do
user.keys << key
user.save
get api("/keys/#{key.id}", user)
response.status.should == 200
json_response["title"].should == key.title
end
it "should return 404 Not Found within invalid ID" do
get api("/keys/42", user)
response.status.should == 404
end
end
describe "POST /keys" do
it "should not create invalid ssh key" do
post api("/keys", user), { title: "invalid key" }
response.status.should == 404
end
it "should create ssh key" do
key_attrs = Factory.attributes :key
expect {
post api("/keys", user), key_attrs
}.to change{ user.keys.count }.by(1)
end
end
describe "DELETE /keys/:id" do
it "should delete existed key" do
user.keys << key
user.save
expect {
delete api("/keys/#{key.id}", user)
}.to change{user.keys.count}.by(-1)
end
it "should return 404 Not Found within invalid ID" do
delete api("/keys/42", user)
response.status.should == 404
end
end
end
...@@ -6,12 +6,14 @@ describe Gitlab::API do ...@@ -6,12 +6,14 @@ describe Gitlab::API do
let(:user) { Factory :user } let(:user) { Factory :user }
describe "GET /users" do describe "GET /users" do
context "when unauthenticated" do
it "should return authentication error" do it "should return authentication error" do
get api("/users") get api("/users")
response.status.should == 401 response.status.should == 401
end end
end
describe "authenticated GET /users" do context "when authenticated" do
it "should return an array of users" do it "should return an array of users" do
get api("/users", user) get api("/users", user)
response.status.should == 200 response.status.should == 200
......
require 'spec_helper' require 'spec_helper'
describe "User Issues Dashboard" do describe "Dashboard Issues Feed" do
describe "GET /issues" do describe "GET /issues" do
before do let!(:user) { Factory :user }
let!(:project1) { Factory :project }
let!(:project2) { Factory :project }
let!(:issue1) { Factory :issue, author: user, assignee: user, project: project1 }
let!(:issue2) { Factory :issue, author: user, assignee: user, project: project2 }
login_as :user describe "atom feed" do
@project1 = Factory :project
@project2 = Factory :project
@project1.add_access(@user, :read, :write)
@project2.add_access(@user, :read, :write)
@issue1 = Factory :issue,
author: @user,
assignee: @user,
project: @project1
@issue2 = Factory :issue,
author: @user,
assignee: @user,
project: @project2
visit dashboard_issues_path
end
describe "atom feed", js: false do
it "should render atom feed via private token" do it "should render atom feed via private token" do
logout visit dashboard_issues_path(:atom, private_token: user.private_token)
visit dashboard_issues_path(:atom, private_token: @user.private_token)
page.response_headers['Content-Type'].should have_content("application/atom+xml") page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", text: "#{@user.name} issues") page.body.should have_selector("title", text: "#{user.name} issues")
page.body.should have_selector("author email", text: @issue1.author_email) page.body.should have_selector("author email", text: issue1.author_email)
page.body.should have_selector("entry summary", text: @issue1.title) page.body.should have_selector("entry summary", text: issue1.title)
page.body.should have_selector("author email", text: @issue2.author_email) page.body.should have_selector("author email", text: issue2.author_email)
page.body.should have_selector("entry summary", text: @issue2.title) page.body.should have_selector("entry summary", text: issue2.title)
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe "User Dashboard" do describe "Dashboard Feed" do
before { login_as :user }
describe "GET /" do describe "GET /" do
before do let!(:user) { Factory :user }
@project = Factory :project, owner: @user
@project.add_access(@user, :read)
visit dashboard_path
end
it "should render projects atom feed via private token" do
logout
visit dashboard_path(:atom, private_token: @user.private_token) context "projects atom feed via private token" do
it "should render projects atom feed" do
visit dashboard_path(:atom, private_token: user.private_token)
page.body.should have_selector("feed title") page.body.should have_selector("feed title")
end end
end
it "should not render projects page via private token" do context "projects page via private token" do
logout it "should redirect to login page" do
visit dashboard_path(private_token: user.private_token)
visit dashboard_path(private_token: @user.private_token)
current_path.should == new_user_session_path current_path.should == new_user_session_path
end end
end end
end
end end
require 'spec_helper' require 'spec_helper'
describe "Issues" do describe "Issues Feed" do
let(:project) { Factory :project }
before do
login_as :user
project.add_access(@user, :read, :write)
end
describe "GET /issues" do describe "GET /issues" do
before do let!(:user) { Factory :user }
@issue = Factory :issue, let!(:project) { Factory :project, owner: user }
author: @user, let!(:issue) { Factory :issue, author: user, project: project }
assignee: @user,
project: project
visit project_issues_path(project) before { project.add_access(user, :read, :write) }
end
context "when authenticated" do
it "should render atom feed" do it "should render atom feed" do
login_with user
visit project_issues_path(project, :atom) visit project_issues_path(project, :atom)
page.response_headers['Content-Type'].should have_content("application/atom+xml") page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", text: "#{project.name} issues") page.body.should have_selector("title", text: "#{project.name} issues")
page.body.should have_selector("author email", text: @issue.author_email) page.body.should have_selector("author email", text: issue.author_email)
page.body.should have_selector("entry summary", text: @issue.title) page.body.should have_selector("entry summary", text: issue.title)
end
end end
it "should render atom feed via private token" do context "when authenticated via private token" do
logout it "should render atom feed" do
visit project_issues_path(project, :atom, private_token: @user.private_token) visit project_issues_path(project, :atom, private_token: user.private_token)
page.response_headers['Content-Type'].should have_content("application/atom+xml") page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", text: "#{project.name} issues") page.body.should have_selector("title", text: "#{project.name} issues")
page.body.should have_selector("author email", text: @issue.author_email) page.body.should have_selector("author email", text: issue.author_email)
page.body.should have_selector("entry summary", text: @issue.title) page.body.should have_selector("entry summary", text: issue.title)
end
end end
end end
end end
...@@ -25,6 +25,7 @@ describe "Gitlab Flavored Markdown" do ...@@ -25,6 +25,7 @@ describe "Gitlab Flavored Markdown" do
@tag_name = "gfm-test-tag" @tag_name = "gfm-test-tag"
r.git.native(:tag, {}, @tag_name, commit.id) r.git.native(:tag, {}, @tag_name, commit.id)
end end
after do after do
# delete test branch and tag # delete test branch and tag
project.repo.git.native(:branch, {D: true}, @branch_name) project.repo.git.native(:branch, {D: true}, @branch_name)
......
...@@ -28,8 +28,8 @@ describe "Users Security" do ...@@ -28,8 +28,8 @@ describe "Users Security" do
it { should be_denied_for :visitor } it { should be_denied_for :visitor }
end end
describe "GET /profile/password" do describe "GET /profile/account" do
subject { profile_password_path } subject { profile_account_path }
it { should be_allowed_for @u1 } it { should be_allowed_for @u1 }
it { should be_allowed_for :admin } it { should be_allowed_for :admin }
......
...@@ -70,7 +70,7 @@ describe "Application access" do ...@@ -70,7 +70,7 @@ describe "Application access" do
end end
describe "GET /project_code/team" do describe "GET /project_code/team" do
subject { team_project_path(@project) } subject { project_team_index_path(@project) }
it { should be_allowed_for @u1 } it { should be_allowed_for @u1 }
it { should be_allowed_for @u3 } it { should be_allowed_for @u3 }
......
require 'spec_helper'
describe Issue, "Upvote" do
let(:issue) { create(:issue) }
it "with no notes has a 0/0 score" do
issue.upvotes.should == 0
end
it "should recognize non-+1 notes" do
issue.notes << create(:note, note: "No +1 here")
issue.should have(1).note
issue.notes.first.upvote?.should be_false
issue.upvotes.should == 0
end
it "should recognize a single +1 note" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.upvotes.should == 1
end
it "should recognize multiple +1 notes" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.notes << create(:note, note: "+1 I want this")
issue.upvotes.should == 2
end
end
require 'spec_helper'
describe Issue do
let(:issue) { create(:issue) }
describe "#upvotes" do
it "with no notes has a 0/0 score" do
issue.upvotes.should == 0
end
it "should recognize non-+1 notes" do
issue.notes << create(:note, note: "No +1 here")
issue.should have(1).note
issue.notes.first.upvote?.should be_false
issue.upvotes.should == 0
end
it "should recognize a single +1 note" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.upvotes.should == 1
end
it "should recognize multiple +1 notes" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.notes << create(:note, note: "+1 I want this")
issue.upvotes.should == 2
end
end
describe "#downvotes" do
it "with no notes has a 0/0 score" do
issue.downvotes.should == 0
end
it "should recognize non--1 notes" do
issue.notes << create(:note, note: "Almost got a -1")
issue.should have(1).note
issue.notes.first.downvote?.should be_false
issue.downvotes.should == 0
end
it "should recognize a single -1 note" do
issue.notes << create(:note, note: "-1 This is bad")
issue.downvotes.should == 1
end
it "should recognize multiple -1 notes" do
issue.notes << create(:note, note: "-1 This is bad")
issue.notes << create(:note, note: "-1 Away with this")
issue.downvotes.should == 2
end
end
describe "#votes_count" do
it "with no notes has a 0/0 score" do
issue.votes_count.should == 0
end
it "should recognize non notes" do
issue.notes << create(:note, note: "No +1 here")
issue.should have(1).note
issue.votes_count.should == 0
end
it "should recognize a single +1 note" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.votes_count.should == 1
end
it "should recognize a single -1 note" do
issue.notes << create(:note, note: "-1 This is bad")
issue.votes_count.should == 1
end
it "should recognize multiple notes" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.notes << create(:note, note: "-1 This is bad")
issue.notes << create(:note, note: "+1 I want this")
issue.votes_count.should == 3
end
end
describe "#upvotes_in_percent" do
it "with no notes has a 0% score" do
issue.upvotes_in_percent.should == 0
end
it "should count a single 1 note as 100%" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.upvotes_in_percent.should == 100
end
it "should count multiple +1 notes as 100%" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.notes << create(:note, note: "+1 I want this")
issue.upvotes_in_percent.should == 100
end
it "should count fractions for multiple +1 and -1 notes correctly" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.notes << create(:note, note: "+1 I want this")
issue.notes << create(:note, note: "-1 This is bad")
issue.notes << create(:note, note: "+1 me too")
issue.upvotes_in_percent.should == 75
end
end
describe "#downvotes_in_percent" do
it "with no notes has a 0% score" do
issue.downvotes_in_percent.should == 0
end
it "should count a single -1 note as 100%" do
issue.notes << create(:note, note: "-1 This is bad")
issue.downvotes_in_percent.should == 100
end
it "should count multiple -1 notes as 100%" do
issue.notes << create(:note, note: "-1 This is bad")
issue.notes << create(:note, note: "-1 Away with this")
issue.downvotes_in_percent.should == 100
end
it "should count fractions for multiple +1 and -1 notes correctly" do
issue.notes << create(:note, note: "+1 This is awesome")
issue.notes << create(:note, note: "+1 I want this")
issue.notes << create(:note, note: "-1 This is bad")
issue.notes << create(:note, note: "+1 me too")
issue.downvotes_in_percent.should == 25
end
end
end
require 'spec_helper'
# team_update_admin_user PUT /admin/users/:id/team_update(.:format) admin/users#team_update
# block_admin_user PUT /admin/users/:id/block(.:format) admin/users#block
# unblock_admin_user PUT /admin/users/:id/unblock(.:format) admin/users#unblock
# admin_users GET /admin/users(.:format) admin/users#index
# POST /admin/users(.:format) admin/users#create
# new_admin_user GET /admin/users/new(.:format) admin/users#new
# edit_admin_user GET /admin/users/:id/edit(.:format) admin/users#edit
# admin_user GET /admin/users/:id(.:format) admin/users#show
# PUT /admin/users/:id(.:format) admin/users#update
# DELETE /admin/users/:id(.:format) admin/users#destroy
describe Admin::UsersController, "routing" do
it "to #team_update" do
put("/admin/users/1/team_update").should route_to('admin/users#team_update', id: '1')
end
it "to #block" do
put("/admin/users/1/block").should route_to('admin/users#block', id: '1')
end
it "to #unblock" do
put("/admin/users/1/unblock").should route_to('admin/users#unblock', id: '1')
end
it "to #index" do
get("/admin/users").should route_to('admin/users#index')
end
it "to #show" do
get("/admin/users/1").should route_to('admin/users#show', id: '1')
end
it "to #create" do
post("/admin/users").should route_to('admin/users#create')
end
it "to #new" do
get("/admin/users/new").should route_to('admin/users#new')
end
it "to #edit" do
get("/admin/users/1/edit").should route_to('admin/users#edit', id: '1')
end
it "to #show" do
get("/admin/users/1").should route_to('admin/users#show', id: '1')
end
it "to #update" do
put("/admin/users/1").should route_to('admin/users#update', id: '1')
end
it "to #destroy" do
delete("/admin/users/1").should route_to('admin/users#destroy', id: '1')
end
end
# team_admin_project GET /admin/projects/:id/team(.:format) admin/projects#team {:id=>/[^\/]+/}
# team_update_admin_project PUT /admin/projects/:id/team_update(.:format) admin/projects#team_update {:id=>/[^\/]+/}
# admin_projects GET /admin/projects(.:format) admin/projects#index {:id=>/[^\/]+/}
# POST /admin/projects(.:format) admin/projects#create {:id=>/[^\/]+/}
# new_admin_project GET /admin/projects/new(.:format) admin/projects#new {:id=>/[^\/]+/}
# edit_admin_project GET /admin/projects/:id/edit(.:format) admin/projects#edit {:id=>/[^\/]+/}
# admin_project GET /admin/projects/:id(.:format) admin/projects#show {:id=>/[^\/]+/}
# PUT /admin/projects/:id(.:format) admin/projects#update {:id=>/[^\/]+/}
# DELETE /admin/projects/:id(.:format) admin/projects#destroy {:id=>/[^\/]+/}
describe Admin::ProjectsController, "routing" do
it "to #team" do
get("/admin/projects/gitlab/team").should route_to('admin/projects#team', id: 'gitlab')
end
it "to #team_update" do
put("/admin/projects/gitlab/team_update").should route_to('admin/projects#team_update', id: 'gitlab')
end
it "to #index" do
get("/admin/projects").should route_to('admin/projects#index')
end
it "to #create" do
post("/admin/projects").should route_to('admin/projects#create')
end
it "to #new" do
get("/admin/projects/new").should route_to('admin/projects#new')
end
it "to #edit" do
get("/admin/projects/gitlab/edit").should route_to('admin/projects#edit', id: 'gitlab')
end
it "to #show" do
get("/admin/projects/gitlab").should route_to('admin/projects#show', id: 'gitlab')
end
it "to #update" do
put("/admin/projects/gitlab").should route_to('admin/projects#update', id: 'gitlab')
end
it "to #destroy" do
delete("/admin/projects/gitlab").should route_to('admin/projects#destroy', id: 'gitlab')
end
end
# edit_admin_team_member GET /admin/team_members/:id/edit(.:format) admin/team_members#edit
# admin_team_member PUT /admin/team_members/:id(.:format) admin/team_members#update
# DELETE /admin/team_members/:id(.:format) admin/team_members#destroy
describe Admin::TeamMembersController, "routing" do
it "to #edit" do
get("/admin/team_members/1/edit").should route_to('admin/team_members#edit', id: '1')
end
it "to #update" do
put("/admin/team_members/1").should route_to('admin/team_members#update', id: '1')
end
it "to #destroy" do
delete("/admin/team_members/1").should route_to('admin/team_members#destroy', id: '1')
end
end
# admin_hook_test GET /admin/hooks/:hook_id/test(.:format) admin/hooks#test
# admin_hooks GET /admin/hooks(.:format) admin/hooks#index
# POST /admin/hooks(.:format) admin/hooks#create
# admin_hook DELETE /admin/hooks/:id(.:format) admin/hooks#destroy
describe Admin::HooksController, "routing" do
it "to #test" do
get("/admin/hooks/1/test").should route_to('admin/hooks#test', hook_id: '1')
end
it "to #index" do
get("/admin/hooks").should route_to('admin/hooks#index')
end
it "to #create" do
post("/admin/hooks").should route_to('admin/hooks#create')
end
it "to #destroy" do
delete("/admin/hooks/1").should route_to('admin/hooks#destroy', id: '1')
end
end
# admin_logs GET /admin/logs(.:format) admin/logs#show
describe Admin::LogsController, "routing" do
it "to #show" do
get("/admin/logs").should route_to('admin/logs#show')
end
end
# admin_resque GET /admin/resque(.:format) admin/resque#show
describe Admin::ResqueController, "routing" do
it "to #show" do
get("/admin/resque").should route_to('admin/resque#show')
end
end
# admin_root /admin(.:format) admin/dashboard#index
describe Admin::DashboardController, "routing" do
it "to #index" do
get("/admin").should route_to('admin/dashboard#index')
end
end
require 'spec_helper'
# Shared examples for a resource inside a Project
#
# By default it tests all the default REST actions: index, create, new, edit,
# show, update, and destroy. You can remove actions by customizing the
# `actions` variable.
#
# It also expects a `controller` variable to be available which defines both
# the path to the resource as well as the controller name.
#
# Examples
#
# # Default behavior
# it_behaves_like "RESTful project resources" do
# let(:controller) { 'issues' }
# end
#
# # Customizing actions
# it_behaves_like "RESTful project resources" do
# let(:actions) { [:index] }
# let(:controller) { 'issues' }
# end
shared_examples "RESTful project resources" do
let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
it "to #index" do
get("/gitlabhq/#{controller}").should route_to("#{controller}#index", project_id: 'gitlabhq') if actions.include?(:index)
end
it "to #create" do
post("/gitlabhq/#{controller}").should route_to("#{controller}#create", project_id: 'gitlabhq') if actions.include?(:create)
end
it "to #new" do
get("/gitlabhq/#{controller}/new").should route_to("#{controller}#new", project_id: 'gitlabhq') if actions.include?(:new)
end
it "to #edit" do
get("/gitlabhq/#{controller}/1/edit").should route_to("#{controller}#edit", project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
end
it "to #show" do
get("/gitlabhq/#{controller}/1").should route_to("#{controller}#show", project_id: 'gitlabhq', id: '1') if actions.include?(:show)
end
it "to #update" do
put("/gitlabhq/#{controller}/1").should route_to("#{controller}#update", project_id: 'gitlabhq', id: '1') if actions.include?(:update)
end
it "to #destroy" do
delete("/gitlabhq/#{controller}/1").should route_to("#{controller}#destroy", project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
end
end
# projects POST /projects(.:format) projects#create
# new_project GET /projects/new(.:format) projects#new
# wall_project GET /:id/wall(.:format) projects#wall
# graph_project GET /:id/graph(.:format) projects#graph
# files_project GET /:id/files(.:format) projects#files
# edit_project GET /:id/edit(.:format) projects#edit
# project GET /:id(.:format) projects#show
# PUT /:id(.:format) projects#update
# DELETE /:id(.:format) projects#destroy
describe ProjectsController, "routing" do
it "to #create" do
post("/projects").should route_to('projects#create')
end
it "to #new" do
get("/projects/new").should route_to('projects#new')
end
it "to #wall" do
get("/gitlabhq/wall").should route_to('projects#wall', id: 'gitlabhq')
end
it "to #graph" do
get("/gitlabhq/graph").should route_to('projects#graph', id: 'gitlabhq')
end
it "to #files" do
get("/gitlabhq/files").should route_to('projects#files', id: 'gitlabhq')
end
it "to #edit" do
get("/gitlabhq/edit").should route_to('projects#edit', id: 'gitlabhq')
end
it "to #show" do
get("/gitlabhq").should route_to('projects#show', id: 'gitlabhq')
end
it "to #update" do
put("/gitlabhq").should route_to('projects#update', id: 'gitlabhq')
end
it "to #destroy" do
delete("/gitlabhq").should route_to('projects#destroy', id: 'gitlabhq')
end
end
# pages_project_wikis GET /:project_id/wikis/pages(.:format) wikis#pages
# history_project_wiki GET /:project_id/wikis/:id/history(.:format) wikis#history
# project_wikis POST /:project_id/wikis(.:format) wikis#create
# edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) wikis#edit
# project_wiki GET /:project_id/wikis/:id(.:format) wikis#show
# DELETE /:project_id/wikis/:id(.:format) wikis#destroy
describe WikisController, "routing" do
it "to #pages" do
get("/gitlabhq/wikis/pages").should route_to('wikis#pages', project_id: 'gitlabhq')
end
it "to #history" do
get("/gitlabhq/wikis/1/history").should route_to('wikis#history', project_id: 'gitlabhq', id: '1')
end
it_behaves_like "RESTful project resources" do
let(:actions) { [:create, :edit, :show, :destroy] }
let(:controller) { 'wikis' }
end
end
# branches_project_repository GET /:project_id/repository/branches(.:format) repositories#branches
# tags_project_repository GET /:project_id/repository/tags(.:format) repositories#tags
# archive_project_repository GET /:project_id/repository/archive(.:format) repositories#archive
# project_repository POST /:project_id/repository(.:format) repositories#create
# new_project_repository GET /:project_id/repository/new(.:format) repositories#new
# edit_project_repository GET /:project_id/repository/edit(.:format) repositories#edit
# GET /:project_id/repository(.:format) repositories#show
# PUT /:project_id/repository(.:format) repositories#update
# DELETE /:project_id/repository(.:format) repositories#destroy
describe RepositoriesController, "routing" do
it "to #branches" do
get("/gitlabhq/repository/branches").should route_to('repositories#branches', project_id: 'gitlabhq')
end
it "to #tags" do
get("/gitlabhq/repository/tags").should route_to('repositories#tags', project_id: 'gitlabhq')
end
it "to #archive" do
get("/gitlabhq/repository/archive").should route_to('repositories#archive', project_id: 'gitlabhq')
end
it "to #create" do
post("/gitlabhq/repository").should route_to('repositories#create', project_id: 'gitlabhq')
end
it "to #new" do
get("/gitlabhq/repository/new").should route_to('repositories#new', project_id: 'gitlabhq')
end
it "to #edit" do
get("/gitlabhq/repository/edit").should route_to('repositories#edit', project_id: 'gitlabhq')
end
it "to #show" do
get("/gitlabhq/repository").should route_to('repositories#show', project_id: 'gitlabhq')
end
it "to #update" do
put("/gitlabhq/repository").should route_to('repositories#update', project_id: 'gitlabhq')
end
it "to #destroy" do
delete("/gitlabhq/repository").should route_to('repositories#destroy', project_id: 'gitlabhq')
end
end
# project_deploy_keys GET /:project_id/deploy_keys(.:format) deploy_keys#index
# POST /:project_id/deploy_keys(.:format) deploy_keys#create
# new_project_deploy_key GET /:project_id/deploy_keys/new(.:format) deploy_keys#new
# edit_project_deploy_key GET /:project_id/deploy_keys/:id/edit(.:format) deploy_keys#edit
# project_deploy_key GET /:project_id/deploy_keys/:id(.:format) deploy_keys#show
# PUT /:project_id/deploy_keys/:id(.:format) deploy_keys#update
# DELETE /:project_id/deploy_keys/:id(.:format) deploy_keys#destroy
describe DeployKeysController, "routing" do
it_behaves_like "RESTful project resources" do
let(:controller) { 'deploy_keys' }
end
end
# project_protected_branches GET /:project_id/protected_branches(.:format) protected_branches#index
# POST /:project_id/protected_branches(.:format) protected_branches#create
# project_protected_branch DELETE /:project_id/protected_branches/:id(.:format) protected_branches#destroy
describe ProtectedBranchesController, "routing" do
it_behaves_like "RESTful project resources" do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'protected_branches' }
end
end
# switch_project_refs GET /:project_id/switch(.:format) refs#switch
# tree_project_ref GET /:project_id/:id/tree(.:format) refs#tree
# logs_tree_project_ref GET /:project_id/:id/logs_tree(.:format) refs#logs_tree
# blob_project_ref GET /:project_id/:id/blob(.:format) refs#blob
# tree_file_project_ref GET /:project_id/:id/tree/:path(.:format) refs#tree
# logs_file_project_ref GET /:project_id/:id/logs_tree/:path(.:format) refs#logs_tree
# blame_file_project_ref GET /:project_id/:id/blame/:path(.:format) refs#blame
describe RefsController, "routing" do
it "to #switch" do
get("/gitlabhq/switch").should route_to('refs#switch', project_id: 'gitlabhq')
end
it "to #tree" do
get("/gitlabhq/stable/tree").should route_to('refs#tree', project_id: 'gitlabhq', id: 'stable')
get("/gitlabhq/stable/tree/foo/bar/baz").should route_to('refs#tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
end
it "to #logs_tree" do
get("/gitlabhq/stable/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable')
get("/gitlabhq/stable/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
end
it "to #blob" do
get("/gitlabhq/stable/blob").should route_to('refs#blob', project_id: 'gitlabhq', id: 'stable')
end
it "to #blame" do
get("/gitlabhq/stable/blame/foo/bar/baz").should route_to('refs#blame', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
end
end
# diffs_project_merge_request GET /:project_id/merge_requests/:id/diffs(.:format) merge_requests#diffs
# automerge_project_merge_request GET /:project_id/merge_requests/:id/automerge(.:format) merge_requests#automerge
# automerge_check_project_merge_request GET /:project_id/merge_requests/:id/automerge_check(.:format) merge_requests#automerge_check
# raw_project_merge_request GET /:project_id/merge_requests/:id/raw(.:format) merge_requests#raw
# branch_from_project_merge_requests GET /:project_id/merge_requests/branch_from(.:format) merge_requests#branch_from
# branch_to_project_merge_requests GET /:project_id/merge_requests/branch_to(.:format) merge_requests#branch_to
# project_merge_requests GET /:project_id/merge_requests(.:format) merge_requests#index
# POST /:project_id/merge_requests(.:format) merge_requests#create
# new_project_merge_request GET /:project_id/merge_requests/new(.:format) merge_requests#new
# edit_project_merge_request GET /:project_id/merge_requests/:id/edit(.:format) merge_requests#edit
# project_merge_request GET /:project_id/merge_requests/:id(.:format) merge_requests#show
# PUT /:project_id/merge_requests/:id(.:format) merge_requests#update
# DELETE /:project_id/merge_requests/:id(.:format) merge_requests#destroy
describe MergeRequestsController, "routing" do
it "to #diffs" do
get("/gitlabhq/merge_requests/1/diffs").should route_to('merge_requests#diffs', project_id: 'gitlabhq', id: '1')
end
it "to #automerge" do
get("/gitlabhq/merge_requests/1/automerge").should route_to('merge_requests#automerge', project_id: 'gitlabhq', id: '1')
end
it "to #automerge_check" do
get("/gitlabhq/merge_requests/1/automerge_check").should route_to('merge_requests#automerge_check', project_id: 'gitlabhq', id: '1')
end
it "to #raw" do
get("/gitlabhq/merge_requests/1/raw").should route_to('merge_requests#raw', project_id: 'gitlabhq', id: '1')
end
it "to #branch_from" do
get("/gitlabhq/merge_requests/branch_from").should route_to('merge_requests#branch_from', project_id: 'gitlabhq')
end
it "to #branch_to" do
get("/gitlabhq/merge_requests/branch_to").should route_to('merge_requests#branch_to', project_id: 'gitlabhq')
end
it_behaves_like "RESTful project resources" do
let(:controller) { 'merge_requests' }
end
end
# raw_project_snippet GET /:project_id/snippets/:id/raw(.:format) snippets#raw
# project_snippets GET /:project_id/snippets(.:format) snippets#index
# POST /:project_id/snippets(.:format) snippets#create
# new_project_snippet GET /:project_id/snippets/new(.:format) snippets#new
# edit_project_snippet GET /:project_id/snippets/:id/edit(.:format) snippets#edit
# project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
# PUT /:project_id/snippets/:id(.:format) snippets#update
# DELETE /:project_id/snippets/:id(.:format) snippets#destroy
describe SnippetsController, "routing" do
it "to #raw" do
get("/gitlabhq/snippets/1/raw").should route_to('snippets#raw', project_id: 'gitlabhq', id: '1')
end
it_behaves_like "RESTful project resources" do
let(:controller) { 'snippets' }
end
end
# test_project_hook GET /:project_id/hooks/:id/test(.:format) hooks#test
# project_hooks GET /:project_id/hooks(.:format) hooks#index
# POST /:project_id/hooks(.:format) hooks#create
# project_hook DELETE /:project_id/hooks/:id(.:format) hooks#destroy
describe HooksController, "routing" do
it "to #test" do
get("/gitlabhq/hooks/1/test").should route_to('hooks#test', project_id: 'gitlabhq', id: '1')
end
it_behaves_like "RESTful project resources" do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'hooks' }
end
end
# compare_project_commits GET /:project_id/commits/compare(.:format) commits#compare
# patch_project_commit GET /:project_id/commits/:id/patch(.:format) commits#patch
# project_commits GET /:project_id/commits(.:format) commits#index
# POST /:project_id/commits(.:format) commits#create
# new_project_commit GET /:project_id/commits/new(.:format) commits#new
# edit_project_commit GET /:project_id/commits/:id/edit(.:format) commits#edit
# project_commit GET /:project_id/commits/:id(.:format) commits#show
# PUT /:project_id/commits/:id(.:format) commits#update
# DELETE /:project_id/commits/:id(.:format) commits#destroy
describe CommitsController, "routing" do
it "to #compare" do
get("/gitlabhq/commits/compare").should route_to('commits#compare', project_id: 'gitlabhq')
end
it "to #patch" do
get("/gitlabhq/commits/1/patch").should route_to('commits#patch', project_id: 'gitlabhq', id: '1')
end
it_behaves_like "RESTful project resources" do
let(:controller) { 'commits' }
end
end
# project_team_members GET /:project_id/team_members(.:format) team_members#index
# POST /:project_id/team_members(.:format) team_members#create
# new_project_team_member GET /:project_id/team_members/new(.:format) team_members#new
# edit_project_team_member GET /:project_id/team_members/:id/edit(.:format) team_members#edit
# project_team_member GET /:project_id/team_members/:id(.:format) team_members#show
# PUT /:project_id/team_members/:id(.:format) team_members#update
# DELETE /:project_id/team_members/:id(.:format) team_members#destroy
describe TeamMembersController, "routing" do
it_behaves_like "RESTful project resources" do
let(:controller) { 'team_members' }
end
end
# project_milestones GET /:project_id/milestones(.:format) milestones#index
# POST /:project_id/milestones(.:format) milestones#create
# new_project_milestone GET /:project_id/milestones/new(.:format) milestones#new
# edit_project_milestone GET /:project_id/milestones/:id/edit(.:format) milestones#edit
# project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
# PUT /:project_id/milestones/:id(.:format) milestones#update
# DELETE /:project_id/milestones/:id(.:format) milestones#destroy
describe MilestonesController, "routing" do
it_behaves_like "RESTful project resources" do
let(:controller) { 'milestones' }
end
end
# project_labels GET /:project_id/labels(.:format) labels#index
describe LabelsController, "routing" do
it "to #index" do
get("/gitlabhq/labels").should route_to('labels#index', project_id: 'gitlabhq')
end
end
# sort_project_issues POST /:project_id/issues/sort(.:format) issues#sort
# bulk_update_project_issues POST /:project_id/issues/bulk_update(.:format) issues#bulk_update
# search_project_issues GET /:project_id/issues/search(.:format) issues#search
# project_issues GET /:project_id/issues(.:format) issues#index
# POST /:project_id/issues(.:format) issues#create
# new_project_issue GET /:project_id/issues/new(.:format) issues#new
# edit_project_issue GET /:project_id/issues/:id/edit(.:format) issues#edit
# project_issue GET /:project_id/issues/:id(.:format) issues#show
# PUT /:project_id/issues/:id(.:format) issues#update
# DELETE /:project_id/issues/:id(.:format) issues#destroy
describe IssuesController, "routing" do
it "to #sort" do
post("/gitlabhq/issues/sort").should route_to('issues#sort', project_id: 'gitlabhq')
end
it "to #bulk_update" do
post("/gitlabhq/issues/bulk_update").should route_to('issues#bulk_update', project_id: 'gitlabhq')
end
it "to #search" do
get("/gitlabhq/issues/search").should route_to('issues#search', project_id: 'gitlabhq')
end
it_behaves_like "RESTful project resources" do
let(:controller) { 'issues' }
end
end
# preview_project_notes POST /:project_id/notes/preview(.:format) notes#preview
# project_notes GET /:project_id/notes(.:format) notes#index
# POST /:project_id/notes(.:format) notes#create
# project_note DELETE /:project_id/notes/:id(.:format) notes#destroy
describe NotesController, "routing" do
it "to #preview" do
post("/gitlabhq/notes/preview").should route_to('notes#preview', project_id: 'gitlabhq')
end
it_behaves_like "RESTful project resources" do
let(:actions) { [:index, :create, :destroy] }
let(:controller) { 'notes' }
end
end
require 'spec_helper'
# search GET /search(.:format) search#show
describe SearchController, "routing" do
it "to #show" do
get("/search").should route_to('search#show')
end
end
# gitlab_api /api Gitlab::API
# resque /info/resque Resque::Server
# /:path Grack
describe "Mounted Apps", "routing" do
it "to API" do
get("/api").should be_routable
end
it "to Resque" do
pending
get("/info/resque").should be_routable
end
it "to Grack" do
get("/gitlabhq.git").should be_routable
end
end
# help GET /help(.:format) help#index
# help_permissions GET /help/permissions(.:format) help#permissions
# help_workflow GET /help/workflow(.:format) help#workflow
# help_api GET /help/api(.:format) help#api
# help_web_hooks GET /help/web_hooks(.:format) help#web_hooks
# help_system_hooks GET /help/system_hooks(.:format) help#system_hooks
# help_markdown GET /help/markdown(.:format) help#markdown
# help_ssh GET /help/ssh(.:format) help#ssh
describe HelpController, "routing" do
it "to #index" do
get("/help").should route_to('help#index')
end
it "to #permissions" do
get("/help/permissions").should route_to('help#permissions')
end
it "to #workflow" do
get("/help/workflow").should route_to('help#workflow')
end
it "to #api" do
get("/help/api").should route_to('help#api')
end
it "to #web_hooks" do
get("/help/web_hooks").should route_to('help#web_hooks')
end
it "to #system_hooks" do
get("/help/system_hooks").should route_to('help#system_hooks')
end
it "to #markdown" do
get("/help/markdown").should route_to('help#markdown')
end
it "to #ssh" do
get("/help/ssh").should route_to('help#ssh')
end
end
# errors_githost GET /errors/githost(.:format) errors#githost
describe ErrorsController, "routing" do
it "to #githost" do
get("/errors/githost").should route_to('errors#githost')
end
end
# profile_account GET /profile/account(.:format) profile#account
# profile_history GET /profile/history(.:format) profile#history
# profile_password PUT /profile/password(.:format) profile#password_update
# profile_token GET /profile/token(.:format) profile#token
# profile_reset_private_token PUT /profile/reset_private_token(.:format) profile#reset_private_token
# profile GET /profile(.:format) profile#show
# profile_design GET /profile/design(.:format) profile#design
# profile_update PUT /profile/update(.:format) profile#update
describe ProfileController, "routing" do
it "to #account" do
get("/profile/account").should route_to('profile#account')
end
it "to #history" do
get("/profile/history").should route_to('profile#history')
end
it "to #password_update" do
put("/profile/password").should route_to('profile#password_update')
end
it "to #token" do
get("/profile/token").should route_to('profile#token')
end
it "to #reset_private_token" do
put("/profile/reset_private_token").should route_to('profile#reset_private_token')
end
it "to #show" do
get("/profile").should route_to('profile#show')
end
it "to #design" do
get("/profile/design").should route_to('profile#design')
end
it "to #update" do
put("/profile/update").should route_to('profile#update')
end
end
# keys GET /keys(.:format) keys#index
# POST /keys(.:format) keys#create
# new_key GET /keys/new(.:format) keys#new
# edit_key GET /keys/:id/edit(.:format) keys#edit
# key GET /keys/:id(.:format) keys#show
# PUT /keys/:id(.:format) keys#update
# DELETE /keys/:id(.:format) keys#destroy
describe KeysController, "routing" do
it "to #index" do
get("/keys").should route_to('keys#index')
end
it "to #create" do
post("/keys").should route_to('keys#create')
end
it "to #new" do
get("/keys/new").should route_to('keys#new')
end
it "to #edit" do
get("/keys/1/edit").should route_to('keys#edit', id: '1')
end
it "to #show" do
get("/keys/1").should route_to('keys#show', id: '1')
end
it "to #update" do
put("/keys/1").should route_to('keys#update', id: '1')
end
it "to #destroy" do
delete("/keys/1").should route_to('keys#destroy', id: '1')
end
end
# dashboard GET /dashboard(.:format) dashboard#index
# dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
# dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
# root / dashboard#index
describe DashboardController, "routing" do
it "to #index" do
get("/dashboard").should route_to('dashboard#index')
get("/").should route_to('dashboard#index')
end
it "to #issues" do
get("/dashboard/issues").should route_to('dashboard#issues')
end
it "to #merge_requests" do
get("/dashboard/merge_requests").should route_to('dashboard#merge_requests')
end
end
# new_user_session GET /users/sign_in(.:format) devise/sessions#new
# user_session POST /users/sign_in(.:format) devise/sessions#create
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
# user_omniauth_authorize /users/auth/:provider(.:format) omniauth_callbacks#passthru
# user_omniauth_callback /users/auth/:action/callback(.:format) omniauth_callbacks#(?-mix:(?!))
# user_password POST /users/password(.:format) devise/passwords#create
# new_user_password GET /users/password/new(.:format) devise/passwords#new
# edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
# PUT /users/password(.:format) devise/passwords#update
describe "Authentication", "routing" do
# pending
end
...@@ -5,42 +5,16 @@ module GitoliteStub ...@@ -5,42 +5,16 @@ module GitoliteStub
end end
def stub_gitolite_admin def stub_gitolite_admin
gitolite_repo = mock( gitolite_admin = double('Gitolite::GitoliteAdmin')
clean_permissions: true, gitolite_admin.as_null_object
add_permission: true
)
gitolite_config = mock(
add_repo: true,
get_repo: gitolite_repo,
has_repo?: true
)
gitolite_admin = double(
'Gitolite::GitoliteAdmin',
config: gitolite_config,
save: true,
)
Gitolite::GitoliteAdmin.stub(new: gitolite_admin) Gitolite::GitoliteAdmin.stub(new: gitolite_admin)
end end
def stub_gitlab_gitolite def stub_gitlab_gitolite
gitolite_config = double('Gitlab::GitoliteConfig') gitolite_config = double('Gitlab::GitoliteConfig')
gitolite_config.stub( gitolite_config.stub(apply: ->() { yield(self) })
apply: ->() { yield(self) }, gitolite_config.as_null_object
write_key: true,
rm_key: true,
update_projects: true,
update_project: true,
update_project!: true,
destroy_project: true,
destroy_project!: true,
admin_all_repo: true,
admin_all_repo!: true,
)
Gitlab::GitoliteConfig.stub(new: gitolite_config) Gitlab::GitoliteConfig.stub(new: gitolite_config)
end end
......
...@@ -73,11 +73,7 @@ module Shoulda::Matchers::ActiveModel ...@@ -73,11 +73,7 @@ module Shoulda::Matchers::ActiveModel
class EnsureLengthOfMatcher class EnsureLengthOfMatcher
# Shortcut for is_at_least and is_at_most # Shortcut for is_at_least and is_at_most
def is_within(range) def is_within(range)
if range.exclude_end? is_at_least(range.min) && is_at_most(range.max)
is_at_least(range.first) && is_at_most(range.last - 1)
else
is_at_least(range.first) && is_at_most(range.last)
end
end end
end end
end end
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