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"
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"
# Supported DBs
......@@ -8,6 +16,10 @@ gem "mysql2"
# Auth
gem "devise", "~> 2.1.0"
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
# GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
......@@ -98,21 +110,28 @@ group :development do
end
group :development, :test do
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara"
gem "capybara-webkit"
gem "headless"
gem "autotest"
gem "autotest-rails"
gem "pry"
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
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
group :test do
gem 'cucumber-rails', :require => false
gem "simplecov", :require => false
gem "shoulda-matchers"
gem 'email_spec'
......
......@@ -68,7 +68,6 @@ GIT
GEM
remote: http://rubygems.org/
specs:
ZenTest (4.8.1)
actionmailer (3.2.8)
actionpack (= 3.2.8)
mail (~> 2.4.4)
......@@ -100,10 +99,6 @@ GEM
rails (~> 3.0)
addressable (2.2.8)
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)
bcrypt-ruby (3.0.1)
blankslate (2.1.2.4)
......@@ -137,16 +132,8 @@ GEM
execjs
coffee-script-source (1.3.3)
colored (1.2)
colorize (0.5.8)
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)
database_cleaner (0.8.0)
devise (2.1.2)
......@@ -171,12 +158,13 @@ GEM
factory_girl_rails (4.0.0)
factory_girl (~> 4.0.0)
railties (>= 3.0.0)
faraday (0.8.4)
multipart-post (~> 1.1)
ffaker (1.14.0)
ffi (1.0.11)
foreman (0.47.0)
thor (>= 0.13.6)
gherkin (2.11.0)
json (>= 1.4.6)
gherkin-ruby (0.2.1)
git (1.2.5)
github-markup (0.7.4)
gitlab_meta (2.9)
......@@ -186,6 +174,15 @@ GEM
multi_xml
rack
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-rails (0.3.4)
actionpack (~> 3.0)
......@@ -199,6 +196,7 @@ GEM
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
httpauth (0.1)
i18n (0.6.1)
journey (1.0.4)
jquery-rails (2.0.2)
......@@ -208,6 +206,8 @@ GEM
jquery-rails
railties (>= 3.1.0)
json (1.7.5)
jwt (0.1.5)
multi_json (>= 1.0)
kaminari (0.14.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
......@@ -219,6 +219,7 @@ GEM
libv8 (3.3.10.4)
libwebsocket (0.1.3)
addressable
listen (0.5.0)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
......@@ -229,12 +230,35 @@ GEM
sprockets (~> 2.0)
multi_json (1.3.6)
multi_xml (0.5.1)
multipart-post (1.1.5)
mysql2 (0.3.11)
net-ldap (0.2.2)
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)
hashie (~> 1.2)
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)
polyglot (0.3.3)
posix-spawn (0.3.6)
......@@ -274,6 +298,9 @@ GEM
raindrops (0.9.0)
rake (0.9.2.2)
raphael-rails (1.5.2)
rb-fsevent (0.9.1)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
rdoc (3.12)
json (~> 1.4)
redcarpet (2.1.1)
......@@ -336,6 +363,13 @@ GEM
tilt (~> 1.3, >= 1.3.3)
six (0.2.0)
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)
hike (~> 1.2)
rack (~> 1.0)
......@@ -378,8 +412,6 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on (= 2.3.1)
annotate!
autotest
autotest-rails
awesome_print
bootstrap-sass (= 2.0.4)
capybara
......@@ -389,7 +421,6 @@ DEPENDENCIES
chosen-rails
coffee-rails (= 3.2.2)
colored
cucumber-rails
database_cleaner
devise (~> 2.1.0)
draper
......@@ -404,6 +435,9 @@ DEPENDENCIES
grack!
grape (~> 0.2.1)
grit!
growl
guard-rspec
guard-spinach
haml-rails
headless
httparty
......@@ -415,12 +449,18 @@ DEPENDENCIES
linguist (~> 1.0.0)!
modernizr (= 2.5.3)
mysql2
omniauth
omniauth-github
omniauth-google-oauth2
omniauth-ldap!
omniauth-twitter
pry
pygments.rb!
rack-mini-profiler
rails (= 3.2.8)
raphael-rails (= 1.5.2)
rb-fsevent
rb-inotify
redcarpet (~> 2.1.1)
resque (~> 1.20.0)
resque_mailer
......@@ -432,6 +472,7 @@ DEPENDENCIES
shoulda-matchers
simplecov
six
spinach-rails
sqlite3
stamp
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 @@ $ ->
elems.val('').attr 'disabled', true
else
elems.removeAttr 'disabled'
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
......@@ -11,7 +11,7 @@
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.waitforimages
//= require bootstrap-modal
//= require bootstrap
//= require modernizr
//= require chosen-jquery
//= require raphael
......
......@@ -24,6 +24,9 @@ $ ->
# Click a .one_click_select field, select the contents
$(".one_click_select").live 'click', -> $(this).select()
# Initialize chosen selects
$('select.chosen').chosen()
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
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 = ->
$('form #project_default_branch').chosen()
disableButtonIfEmptyField '#project_name', '.project-submit'
# Git clone panel switcher
$ ->
# Git clone panel switcher
scope = $ '.project_clone_holder'
if scope.length > 0
$('a, button', scope).click ->
$('a, button', scope).removeClass 'active'
$(@).addClass 'active'
$('#project_clone', scope).val $(@).data 'clone'
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
......@@ -145,6 +145,19 @@ span.update-author {
.label {
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 {
background-color: #eee;
border: 1px solid #ccc;
......@@ -158,6 +171,18 @@ span.update-author {
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 {
......@@ -181,11 +206,12 @@ span.update-author {
}
&.joined {
background-color: #1cb9ff;
background-color: #1ca9dd;
}
&.left {
background-color: #ff5057;
background-color: #888;
float:none;
}
}
......@@ -414,13 +440,48 @@ p.time {
}
}
.upvotes {
font-size: 14px;
font-weight: bold;
color: #468847;
text-align: right;
padding: 4px;
margin: 2px;
.votes {
font-size: 13px;
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;
}
.downvotes {
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) */
......@@ -624,7 +685,7 @@ li.note {
margin-right:40px;
.prev {
@extend .borders;
@extend .thumbnail;
height:120px;
width:175px;
margin-bottom:10px;
......@@ -653,3 +714,31 @@ li.note {
text-align:center;
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 @@
border-color: #CCC;
@include solid_shade;
&.white {
background:#fff;
}
ul {
margin:0;
}
......@@ -142,4 +146,8 @@
border:none;
}
}
.ui-box-body {
padding:10px;
}
}
......@@ -33,7 +33,29 @@
.nav-pills a:hover { background-color:#888; }
.nav-pills .active a { background-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-message { @extend .alert; }
......@@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; }
/** HELPERS **/
.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; }
/** 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;
*/
@import "common.scss";
/**
* Styles related to specific part of app
*/
......@@ -161,6 +160,11 @@ $hover: #fdf5d9;
*/
@import "sections/notes.scss";
/**
* This file represent profile styles
*/
@import "sections/profile.scss";
/**
* Devise styles
*/
......
......@@ -12,35 +12,45 @@
width:120px;
}
.project-refs-form .chzn-container {
.project-refs-form .chzn-container {
position: relative;
top: 0;
left: 0;
margin-right: 10px;
.chzn-drop {
.chzn-drop {
margin:7px 0;
border: 1px solid #CCC;
min-width: 300px;
min-width: 400px;
border: 2px solid $blue_link;
@include border-radius(4px);
.chzn-results {
.chzn-results {
max-height:300px;
.group-result {
color: $blue_link;
}
.active-result {
&.highlighted {
background: $blue_link;
}
}
}
.chzn-search input {
min-width:200px;
min-width:365px;
}
}
.chzn-single {
.chzn-single {
@include bg-gray-gradient;
div {
div {
background:transparent;
border-left:none;
}
span {
span {
font-weight: normal;
}
}
......
.issue_form_box {
.issue_form_box {
@extend .main_box;
.issue_title {
.issue_title {
@extend .top_box_content;
.clearfix {
margin-bottom:0px;
input {
.clearfix {
margin-bottom:0px;
input {
@extend .span8;
}
}
}
.issue_middle_block {
.issue_middle_block {
@extend .middle_box_content;
height:30px;
.issue_assignee {
.issue_assignee {
@extend .span6;
float:left;
}
.issue_milestone {
.issue_milestone {
@extend .span4;
float:left;
}
}
.issue_description {
.issue_description {
@extend .bottom_box_content;
}
}
.issues_table {
.issue {
.issues_table {
.issue {
padding:7px 10px;
.issue_check {
.issue_check {
float:left;
padding: 8px 0;
padding-right: 8px;
min-width: 15px;
}
p {
p {
padding-top:0;
padding-bottom:2px;
}
img.avatar {
img.avatar {
width:32px;
margin-top:4px;
}
}
}
input.check_all_issues {
input.check_all_issues {
float:left;
padding: 0;
margin:0;
......@@ -59,8 +59,8 @@ input.check_all_issues {
height: 22px;
}
.issues_content {
.title {
.issues_content {
.title {
height: 40px;
}
}
......@@ -70,30 +70,30 @@ input.check_all_issues {
@media (min-width: 1200px) { .issues_filters select { width:220px; } }
#issues-table-holder {
.issues_filters {
form {
#issues-table-holder {
.issues_filters {
form {
padding:0;
margin:0;
margin-top:7px
}
}
}
.issues_bulk_update {
.issues_bulk_update {
margin: 0;
form {
form {
padding:0;
margin:0;
margin-top:7px
}
.update_selected_issues {
.update_selected_issues {
position:relative;
top:-2px;
margin-left:4px;
float:left;
}
.update_issues_text {
.update_issues_text {
padding:3px;
line-height: 18px;
float:left;
......@@ -101,10 +101,11 @@ input.check_all_issues {
}
}
#update_status {
#update_status {
width:100px;
}
/**
* Milestones list
*
......
/**
/**
* MR form
*
*/
.mr_branch_box {
.mr_branch_box {
@extend .ui-box;
margin-bottom:20px;
.body {
.body {
background:#f1f1f1;
}
......@@ -17,19 +17,19 @@
* MR -> show: Automerge widget
*
*/
.automerge_widget {
&.can_be_merged {
.automerge_widget {
&.can_be_merged {
background: #DFF0D8;
}
form {
form {
margin-bottom:0;
.clearfix {
.clearfix {
margin-bottom:0;
}
}
.accept_group {
.accept_group {
float:left;
border: 1px solid #ADA;
padding: 2px;
......@@ -37,29 +37,29 @@
border-radius: 5px;
background: #CEB;
.accept_merge_request {
.accept_merge_request {
font-size:13px;
float:left;
}
.remove_branch_holder {
.remove_branch_holder {
margin-left:20px;
margin-right:10px;
float:left;
}
label {
label {
color:#444;
}
}
.how_to_merge_link {
.how_to_merge_link {
@extend .primary;
}
}
.mr_nav_tabs {
li {
a {
.mr_nav_tabs {
li {
a {
font-weight:bold;
padding:8px 20px;
text-align:center;
......@@ -67,19 +67,19 @@
}
}
li.merge_request {
li.merge_request {
padding:7px 10px;
img.avatar {
img.avatar {
width: 32px;
margin-top: 4px;
}
p {
p {
padding: 0px;
padding-bottom: 2px;
}
}
.merge_in_progress {
.merge_in_progress {
@extend .padded;
@extend .append-bottom-10;
}
......@@ -88,22 +88,21 @@ li.merge_request {
@include round-borders-all(4px);
padding:2px 4px;
border:none;
font-size:13px;
font-size:14px;
background: #474D57;
color:#fff;
font-weight:bold;
font-family: monospace;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
}
.mr_source_commit,
.mr_target_commit {
.commit {
.mr_source_commit,
.mr_target_commit {
.commit {
margin:0;
padding:0;
padding: 5px;
margin-bottom: 5px;
.avatar { position:relative }
.row_title {
.row_title {
color:#444;
}
.commit-author-name,
......@@ -113,12 +112,12 @@ li.merge_request {
display:none;
}
list-style:none;
&:hover {
&:hover {
background:none;
}
}
}
.mr_direction_tip {
.mr_direction_tip {
margin-top:40px
}
......@@ -55,7 +55,6 @@ ul.main_menu {
&.current {
background-color:#D5D5D5;
border-bottom: 1px solid #AAA;
border-right: 1px solid #BBB;
border-left: 1px solid #BBB;
border-radius: 0 0 1px 1px;
......
......@@ -3,17 +3,13 @@
*
*/
#notes-list,
#new_notes_list {
#new-notes-list {
display:block;
list-style:none;
margin:0px;
padding:0px;
}
#new_notes_list li:last-child{
border-bottom:1px solid #aaa;
}
.issue_notes,
.wiki_notes {
.note_content {
......@@ -30,9 +26,6 @@
}
#new_note {
.note-text {
height:40px;
}
.attach_holder {
display:none;
}
......@@ -48,7 +41,6 @@
.note {
padding: 8px 0;
border-bottom: 1px solid #eee;
overflow: hidden;
display: block;
img {float: left; margin-right: 10px;}
......@@ -70,6 +62,23 @@
.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{
......@@ -213,7 +222,7 @@ td .line_note_link {
}
}
.note-text {
.note-text {
border: 1px solid #aaa;
box-shadow:none;
}
.profile_history {
.event_feed {
min-height:20px;
.avatar {
width:20px;
}
}
}
......@@ -3,30 +3,31 @@ module Notes
def execute
target_type = params[:target_type]
target_id = params[:target_id]
first_id = params[:first_id]
last_id = params[:last_id]
after_id = params[:after_id]
before_id = params[:before_id]
@notes = case target_type
when "commit"
then 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 "commit"
project.commit_notes(project.commit(target_id)).fresh.limit(20)
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"
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"
then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
project.wiki_notes.limit(20)
end
@notes = if last_id
@notes.where("id > ?", last_id)
elsif first_id
@notes.where("id < ?", first_id)
else
@notes = if after_id
@notes.where("id > ?", after_id)
elsif before_id
@notes.where("id < ?", before_id)
else
@notes
end
end
......
class Admin::DashboardController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::DashboardController < AdminController
def index
@workers = Resque.workers
@pending_jobs = Resque.size(:post_receive)
......
class Admin::HooksController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::HooksController < AdminController
def index
@hooks = SystemHook.all
@hook = SystemHook.new
......@@ -15,7 +11,7 @@ class Admin::HooksController < ApplicationController
redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
else
@hooks = SystemHook.all
render :index
render :index
end
end
......
class Admin::LogsController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::LogsController < AdminController
end
class Admin::ProjectsController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::ProjectsController < AdminController
before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update]
def index
......@@ -43,7 +40,7 @@ class Admin::ProjectsController < ApplicationController
def update
owner_id = params[:project].delete(:owner_id)
if owner_id
if owner_id
@admin_project.owner = User.find(owner_id)
end
......@@ -60,7 +57,7 @@ class Admin::ProjectsController < ApplicationController
redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
end
private
private
def admin_project
@admin_project = Project.find_by_code(params[:id])
......
class Admin::ResqueController < ApplicationController
layout 'admin'
class Admin::ResqueController < AdminController
def show
end
end
\ No newline at end of file
end
class Admin::TeamMembersController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::TeamMembersController < AdminController
def edit
@admin_team_member = UsersProject.find(params[:id])
end
......
class Admin::UsersController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::UsersController < AdminController
def index
@admin_users = User.scoped
@admin_users = @admin_users.filter(params[:filter])
......@@ -24,7 +20,7 @@ class Admin::UsersController < ApplicationController
@admin_user = User.find(params[:id])
UsersProject.user_bulk_import(
@admin_user,
@admin_user,
params[:project_ids],
params[:project_access]
)
......@@ -41,22 +37,22 @@ class Admin::UsersController < ApplicationController
@admin_user = User.find(params[:id])
end
def block
def block
@admin_user = User.find(params[:id])
if @admin_user.block
redirect_to :back, alert: "Successfully blocked"
else
else
redirect_to :back, alert: "Error occured. User was not blocked"
end
end
def unblock
def unblock
@admin_user = User.find(params[:id])
if @admin_user.update_attribute(:blocked, false)
redirect_to :back, alert: "Successfully unblocked"
else
else
redirect_to :back, alert: "Error occured. User was not unblocked"
end
end
......
# 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
abilities << Ability
end
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_project!(action)
return access_denied! unless can?(current_user, action, project)
end
......
......@@ -64,7 +64,7 @@ class CommitsController < ApplicationController
@commit.to_patch,
type: "text/plain",
disposition: 'attachment',
filename: "#{@commit.id.patch}"
filename: "#{@commit.id}.patch"
)
end
......
......@@ -17,7 +17,7 @@ class IssuesController < ApplicationController
before_filter :authorize_write_issue!, only: [:new, :create]
# Allow modify issue
before_filter :authorize_modify_issue!, only: [:close, :edit, :update]
before_filter :authorize_modify_issue!, only: [:edit, :update]
# Allow destroy issue
before_filter :authorize_admin_issue!, only: [:destroy]
......@@ -87,8 +87,6 @@ class IssuesController < ApplicationController
end
def destroy
return access_denied! unless can?(current_user, :admin_issue, @issue)
@issue.destroy
respond_to do |format|
......
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
def failure_message
......@@ -9,7 +14,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
error ||= env["omniauth.error.type"].to_s
error.to_s.humanize if error
end
def ldap
# We only find ourselves here if the authentication to LDAP was successful.
@user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user)
......@@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
sign_in_and_redirect @user
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
......@@ -16,9 +16,6 @@ class ProfileController < ApplicationController
def token
end
def password
end
def password_update
params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
......@@ -32,10 +29,14 @@ class ProfileController < ApplicationController
def reset_private_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
private
private
def user
@user = current_user
......
......@@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController
# Authorize
before_filter :add_project_abilities
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
@team_member = project.users_projects.find(params[:id])
......@@ -22,7 +25,7 @@ class TeamMembersController < ApplicationController
params[:project_access]
)
redirect_to team_project_path(@project)
redirect_to project_team_index_path(@project)
end
def update
......@@ -32,7 +35,7 @@ class TeamMembersController < ApplicationController
unless @team_member.valid?
flash[:alert] = "User should have at least one role"
end
redirect_to team_project_path(@project)
redirect_to project_team_index_path(@project)
end
def destroy
......@@ -40,7 +43,7 @@ class TeamMembersController < ApplicationController
@team_member.destroy
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 }
end
end
......
......@@ -16,7 +16,7 @@ class CommitDecorator < ApplicationDecorator
# In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
return no_commit_message unless safe_message
return no_commit_message if safe_message.blank?
title_end = safe_message.index(/\n/)
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
......
......@@ -62,7 +62,7 @@ module ApplicationHelper
{ 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} / 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
......@@ -104,7 +104,8 @@ module ApplicationHelper
# Profile Area
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 :design; current_page?(controller: "profile", action: :design)
when :ssh_keys; controller.controller_name == "keys"
......@@ -135,4 +136,10 @@ module ApplicationHelper
"Never"
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
......@@ -11,7 +11,9 @@ module GitlabMarkdownHelper
# explicitly produce the correct linking behavior (i.e.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
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|
"</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
def grouper_project_members(project)
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
end
def remove_from_team_message(project, member)
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
end
end
......@@ -8,7 +8,7 @@ module TabHelper
end
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)
end
......
......@@ -18,7 +18,8 @@ module TreeHelper
end
def tree_full_path(content)
if params[:path]
content.name.force_encoding('utf-8')
if params[:path]
File.join(params[:path], content.name)
else
content.name
......
......@@ -35,13 +35,21 @@ class Event < ActiveRecord::Base
end
# Next events currently enabled for system
# - push
# - push
# - new issue
# - merge request
def allowed?
push? || issue? || merge_request? || membership_changed?
end
def project_name
if project
project.name
else
"(deleted)"
end
end
def push?
action == self.class::Pushed && valid_push?
end
......@@ -58,31 +66,31 @@ class Event < ActiveRecord::Base
action == self.class::Reopened
end
def issue?
def issue?
target_type == "Issue"
end
def merge_request?
def merge_request?
target_type == "MergeRequest"
end
def new_issue?
target_type == "Issue" &&
def new_issue?
target_type == "Issue" &&
action == Created
end
def new_merge_request?
target_type == "MergeRequest" &&
def new_merge_request?
target_type == "MergeRequest" &&
action == Created
end
def changed_merge_request?
target_type == "MergeRequest" &&
def changed_merge_request?
target_type == "MergeRequest" &&
[Closed, Reopened].include?(action)
end
def changed_issue?
target_type == "Issue" &&
def changed_issue?
target_type == "Issue" &&
[Closed, Reopened].include?(action)
end
......@@ -98,7 +106,7 @@ class Event < ActiveRecord::Base
joined? || left?
end
def issue
def issue
target if target_type == "Issue"
end
......@@ -106,7 +114,7 @@ class Event < ActiveRecord::Base
target if target_type == "MergeRequest"
end
def author
def author
@author ||= User.find(author_id)
end
......@@ -119,7 +127,7 @@ class Event < ActiveRecord::Base
'joined'
elsif left?
'left'
else
else
"opened"
end
end
......
class Issue < ActiveRecord::Base
include IssueCommonality
include Upvote
include Votes
acts_as_taggable_on :labels
......
......@@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit")
class MergeRequest < ActiveRecord::Base
include IssueCommonality
include Upvote
include Votes
BROKEN_DIFF = "--broken-diff"
......
......@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base
scope :today, where("created_at >= :date", date: Date.today)
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
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, includes(:author)
......@@ -105,6 +105,12 @@ class Note < ActiveRecord::Base
def upvote?
note.start_with?('+1') || note.start_with?(':+1:')
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
# == Schema Information
#
......
......@@ -171,6 +171,10 @@ class Project < ActiveRecord::Base
end
end
def wiki_notes
Note.where(noteable_id: wikis.map(&:id), noteable_type: 'Wiki', project_id: self.id)
end
def project_id
self.id
end
......
......@@ -16,7 +16,7 @@ class Tree
def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path,
@tree = if path
raw_tree / path
raw_tree / path.dup.force_encoding('ascii-8bit')
else
raw_tree
end
......
......@@ -86,33 +86,20 @@ class User < ActiveRecord::Base
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end
def self.find_for_ldap_auth(auth, signed_in_resource=nil)
uid = auth.info.uid
provider = auth.provider
name = auth.info.name.force_encoding("utf-8")
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
# 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
def self.create_from_omniauth(auth, ldap = false)
gitlab_auth.create_from_omniauth(auth, ldap)
end
def self.find_or_new_for_omniauth(auth)
gitlab_auth.find_or_new_for_omniauth(auth)
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
def self.search query
......@@ -148,4 +135,3 @@ end
# bio :string(255)
# blocked :boolean(1) default(FALSE), not null
#
......@@ -28,7 +28,6 @@ class Wiki < ActiveRecord::Base
end
new_wiki
end
end
end
# == Schema Information
......
......@@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer
end
def after_destroy(project)
log_info("Project \"#{project.name}\" was removed")
project.destroy_repository
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
class UserObserver < ActiveRecord::Observer
def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
Notify.new_user_email(user.id, user.password).deliver
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
......@@ -14,8 +14,8 @@ class UsersProjectObserver < ActiveRecord::Observer
def after_destroy(users_project)
Event.create(
project_id: users_project.project.id,
action: Event::Left,
project_id: users_project.project.id,
action: Event::Left,
author_id: users_project.user.id
)
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
.file_title
%i.icon-file
githost.log
.file_content.logs
%ol
- Gitlab::Logger.read_latest.each do |line|
%li
%p= line
%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
%i.icon-file
githost.log
.file_content.logs
%ol
- 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
%p= line
......@@ -32,7 +32,7 @@
- unless project.new_record?
.clearfix
= 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?
.clearfix
......@@ -69,7 +69,6 @@
:javascript
$(function(){
$('#project_owner_id').chosen();
new Projects();
})
......@@ -71,25 +71,11 @@
%th Project Access:
%tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select"
%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 chosen span3"}
%tr
%td= submit_tag 'Add', class: "btn primary"
%td
Read more about project permissions
%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
%iframe{src: resque_url, width: 1168, height: 600, style: "border: none"}
%h3.page_title Resque
%br
.ui-box
%iframe{src: resque_url, width: '100%', height: 600, style: "border: none"}
......@@ -8,20 +8,9 @@
.clearfix
%label Project Access:
.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
.actions
= f.submit 'Save', class: "btn primary"
= 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 @@
%th Project Access:
%tr
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select"
%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 chosen span3"
%tr
%td= submit_tag 'Add', class: "btn primary"
......@@ -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= 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"
: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 @@
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
%strong Browse Code »
%h3.commit-title.page_title
= gfm @commit.title
= gfm escape_once(@commit.title)
- if @commit.description.present?
%pre.commit-description
= gfm @commit.description
= gfm escape_once(@commit.description)
.commit-info
.row
.span4
......
%ul.nav.nav-tabs
%li
= 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= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
= link_to project_commits_path(@project) do
Commits
......@@ -20,14 +16,8 @@
Tags
%span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token
%li.right
%span.rss-icon
= 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"
:javascript
$(function(){
$('.project-refs-select').chosen();
});
......@@ -13,14 +13,11 @@
%td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- 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.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
- 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?
- comments.each_with_index do |note, i|
= 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 "notes/per_line_notes_with_reply", notes: comments
= render "commits/commit_box"
= 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"
......
......@@ -31,13 +31,19 @@
%span= project_last_activity(project)
.bottom= paginate @projects, theme: "gitlab"
%hr
%div
%span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
= image_tag "rss_ui.png", title: "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
%h3.nothing_here_message There are no projects you have access to.
%br
......
......@@ -15,7 +15,7 @@
$(function() {
$('#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.password_field :password, :class => "text bottom", :placeholder => "Password"
- if devise_mapping.rememberable?
......
......@@ -15,7 +15,7 @@
.right
= render :partial => "devise/shared/links"
- if devise_mapping.omniauthable?
%hr/
- resource_class.omniauth_providers.each do |provider|
%hr/
= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary"
%br/
%span
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
......@@ -5,4 +5,4 @@
%strong.cdark= commit.author_name
&ndash;
= 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 @@
.event_lp
%div
= image_tag gravatar_icon(event.author_email), class: "avatar"
%span Your pushed to
%span Your pushed to
= event.ref_type
= link_to project_commits_path(event.project, ref: event.ref_name) do
%strong= truncate(event.ref_name, length: 28)
......
......@@ -2,7 +2,7 @@
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
project
%strong= link_to event.project.name, event.project
%strong= link_to event.project_name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
......
......@@ -18,12 +18,12 @@
= f.label :assignee_id do
%i.icon-user
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
= f.label :milestone_id do
%i.icon-time
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
.clearfix
......
......@@ -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)
.right
- issue.labels.each do |label|
%span.label.label-issue.grouped
%span.label.label-tag.grouped
%i.icon-tag
= label.name
- if issue.notes.any?
......@@ -34,5 +34,5 @@
- else
&nbsp;
- if issue.upvotes > 0
%span.badge.badge-success= "+#{issue.upvotes}"
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
= render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
});
......@@ -12,7 +12,7 @@
= 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 :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
......
= render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
});
......@@ -8,22 +8,22 @@
%span.right
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
- 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
= 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
= 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
Edit
%br
- if @issue.upvotes > 0
.upvotes#upvotes= "+#{pluralize @issue.upvotes, 'upvote'}"
.right
.span3#votes= render 'votes/votes_block', votable: @issue
.back_link
= link_to project_issues_path(@project) do
&larr; To issues list
.main_box
.top_box_content
%h4
......@@ -31,7 +31,7 @@
.alert-message.error.status_info Closed
- else
.alert-message.success.status_info Open
= gfm @issue.title
= gfm escape_once(@issue.title)
.middle_box_content
%cite.cgray Created by
......@@ -61,4 +61,4 @@
= 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
%strong= label.name
%strong
%i.icon-tag
= label.name
.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 @@
%li.home{class: tab_class(:profile)}
= link_to "Profile", profile_path
%li{class: tab_class(:password)}
= link_to "Password", profile_password_path
%li{class: tab_class(:account)}
= link_to "Account", profile_account_path
%li{class: tab_class(:ssh_keys)}
= link_to keys_path do
SSH Keys
%span.count= current_user.keys.count
%li{class: tab_class(:token)}
= link_to "Token", profile_token_path
%li{class: tab_class(:design)}
= link_to "Design", profile_design_path
%li{class: tab_class(:history)}
= link_to "History", profile_history_path
.content
= yield
......@@ -16,7 +16,7 @@
.padded
= f.label :source_branch, "From", class: "control-label"
.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
.span2
......@@ -28,7 +28,7 @@
.padded
= f.label :target_branch, "To", class: "control-label"
.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
%h4.cdark 2. Fill info
......@@ -43,7 +43,7 @@
= f.label :assignee_id do
%i.icon-user
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
......@@ -56,18 +56,12 @@
= link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do
Cancel
:javascript
$(function(){
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 target_branch = $("#merge_request_target_branch");
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() });
......@@ -79,4 +73,3 @@
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() });
});
});
......@@ -23,5 +23,6 @@
authored by #{merge_request.author_name}
= time_ago_in_words(merge_request.created_at)
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 @@
%i.icon-list-alt
Diff
.merge_request_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render("notes/notes", tid: @merge_request.id, tt: "merge_request")
.merge_request_notes.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")
.merge-request-diffs
= render "merge_requests/show/diffs" if @diffs
.status
......
= render "show"
:javascript
$(function(){
PerLineNotes.init();
});
:plain
$(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}");
$(function(){
PerLineNotes.init();
});
: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 @@
.alert-message.error.status_info Closed
- else
.alert-message.success.status_info Open
= gfm @merge_request.title
= gfm escape_once(@merge_request.title)
.middle_box_content
%div
......
......@@ -23,10 +23,8 @@
%i.icon-edit
Edit
%br
- if @merge_request.upvotes > 0
.upvotes#upvotes= "+#{pluralize @merge_request.upvotes, 'upvote'}"
.right
.span3#votes= render 'votes/votes_block', votable: @merge_request
.back_link
= link_to project_merge_requests_path(@project) do
......
= render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
});
......@@ -21,7 +21,7 @@
.alert-message.error.status_info Closed
- else
.alert-message.success.status_info Open
= gfm @milestone.title
= gfm escape_once(@milestone.title)
%small.right= @milestone.expires_at
.middle_box_content
......
......@@ -14,7 +14,7 @@
.right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.clearfix
.row.note_advanced_opts.hide
.row.note_advanced_opts
.span3
= 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'
......
......@@ -5,8 +5,9 @@
$('.note-form-holder #preview-link').text('Preview');
$('.note-form-holder #preview-note').hide();
$('.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
: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"
%div.note-author
%strong= note.author_name
......@@ -6,8 +6,16 @@
%cite.cgray
= time_ago_in_words(note.updated_at)
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)
= 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
Remove
......
- if can? current_user, :write_note, @project
= render "notes/form"
.clear
%hr
%ul#new_notes_list
%ul#notes-list
.status
- @notes.each do |note|
- next unless note.author
= render "note", note: note
: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
%td{colspan: 3}
%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
%td{colspan: 3}
%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
= render "create_line", note: @note
= render "create_per_line_note", note: @note
- else
= render "create_common", note: @note
= render "create_common_note", note: @note
-# Enable submit button
: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 @@
%small
= @user.email
%hr
= form_for @user, url: profile_update_path, method: :put, html: { class: "edit_user form-horizontal" } do |f|
......@@ -28,7 +27,23 @@
= f.text_field :email, class: "input-xlarge"
%span.help-block We also use email for avatar detection.
%hr
.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
.row
.span7
.control-group
= f.label :skype, class: "control-label"
.controls= f.text_field :skype, class: "input-xlarge"
......@@ -44,27 +59,24 @@
= f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250
%span.help-block Tell us about yourself in fewer than 250 characters.
.span5.right
.ui-box.white
.ui-box-body
%h4
Personal projects:
%small.right
%span= current_user.my_own_projects.count
of
%span= current_user.projects_limit
.progress
.bar{style: "width: #{current_user.projects_limit_percent}%;"}
-unless Gitlab.config.disable_gravatar?
%p.alert.alert-info
%strong Tip:
You can change your avatar at gravatar.com
%h4
Personal projects:
%small.right
%span= current_user.my_own_projects.count
of
%span= current_user.projects_limit
.progress
.bar{style: "width: #{current_user.projects_limit_percent}%;"}
%h4
SSH public keys:
%small.right
%span= link_to current_user.keys.count, keys_path
.ui-box.white
.ui-box-body
%h4
SSH public keys:
%strong.right= 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
= 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 @@
= link_to project_path(@project), class: "activities-tab tab" do
%i.icon-home
Show
%li{ class: " #{'active' if (controller.controller_name == "team_members") || current_page?(team_project_path(@project)) }" }
= link_to team_project_path(@project), class: "team-tab tab" do
%li{ class: " #{'active' if (controller.controller_name == "team_members") || current_page?(project_team_index_path(@project)) }" }
= link_to project_team_index_path(@project), class: "team-tab tab" do
%i.icon-user
Team
%li{ class: "#{'active' if current_page?(files_project_path(@project)) }" }
......
......@@ -3,10 +3,10 @@
%h3.page_title Edit Project
%hr
= render "projects/form"
%div.ajax_loader.hide
%div.save-project-loader.hide
%center
%div.padded= image_tag "ajax_loader.gif"
%h3.prepend-top Saving project &amp; repository. Please wait...
= image_tag "ajax_loader.gif"
%h3 Saving project. Please wait a few minutes
:javascript
$(function(){ new Projects(); });
%div.wall_page
= render "notes/notes", tid: nil, tt: "wall"
= render "notes/reversed_notes_with_form", tid: nil, tt: "wall"
......@@ -19,7 +19,7 @@
.entry.clearfix
= f.label :name, "Branch"
.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;
= f.submit 'Protect', class: "primary btn"
......@@ -46,6 +46,3 @@
%td
- if can? current_user, :admin_project, @project
= 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
%li
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form", remote: true do
= 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]
= render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]}
%li{class: "#{'active' if (controller.controller_name == "refs") }"}
= link_to tree_project_ref_path(@project, @ref) do
Source
......
......@@ -47,17 +47,15 @@
:javascript
$(function(){
$('.project-refs-select').chosen();
history.pushState({ path: this.path }, '', "#{@history_path}");
});
// Load last commit log for each file in tree
$(window).load(function(){
ajaxGet('#{@logs_path}');
});
- unless tree.is_blob?
:javascript
// Load last commit log for each file in tree
$(window).load(function(){
ajaxGet('#{@logs_path}');
});
- if params[:path] && request.xhr?
:javascript
......
......@@ -2,7 +2,7 @@
.file_title
%i.icon-file
%span.file_name
= name
= name.force_encoding('utf-8')
%small #{file.mode}
%span.options
= link_to "raw", blob_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small", target: "_blank"
......
......@@ -38,8 +38,3 @@
= preserve do
%pre
= Gitlab::Encode.utf8 lines.join("\n")
:javascript
$(function(){
$('.project-refs-select').chosen();
});
......@@ -11,7 +11,7 @@
%code= commit.short_id
= 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
= time_ago_in_words(commit.committed_date)
ago
......
......@@ -13,7 +13,7 @@
= link_to project_commits_path(@project, commit.id) do
%code= commit.short_id
= 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
%span.right.cgray
= time_ago_in_words(commit.committed_date)
......
......@@ -17,7 +17,7 @@
= link_to project_commit_path(@project, commit.id) do
%code= commit.short_id
= 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
%span.update-author.right
= time_ago_in_words(commit.committed_date)
......
......@@ -3,8 +3,8 @@
= label_tag :search do
%strong Looking for
.input
= text_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge", id: "dashboard_search"
= submit_tag 'Search', class: "btn primary"
= search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
= submit_tag 'Search', class: "btn primary wide"
- if params[:search].present?
%br
%h3
......@@ -15,8 +15,9 @@
.row
.span6
%table
%tr
%th Projects
%thead
%tr
%th Projects
%tbody
- @projects.each do |project|
%tr
......@@ -32,8 +33,9 @@
%h4.nothing_here_message No Projects
%br
%table
%tr
%th Merge Requests
%thead
%tr
%th Merge Requests
%tbody
- @merge_requests.each do |merge_request|
%tr
......@@ -50,8 +52,9 @@
%h4.nothing_here_message No Merge Requests
.span6
%table
%tr
%th Issues
%thead
%tr
%th Issues
%tbody
- @issues.each do |issue|
%tr
......
= 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
:javascript
$(function(){
$('.project-refs-select').chosen();
})
- if respond_to?(:path)
= hidden_field_tag :path, path
......@@ -16,7 +16,7 @@
.input= f.text_field :file_name, placeholder: "example.rb"
.clearfix
= 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
= f.label :content, "Code"
.input= f.text_area :content, class: "span8"
......@@ -26,11 +26,3 @@
= link_to "Cancel", project_snippets_path(@project), class: " btn"
- 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}"
:javascript
$(function(){
$('select#snippet_expires_at').chosen();
});
......@@ -17,4 +17,4 @@
%div{class: current_user.dark_scheme ? "black" : ""}
= 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 @@
%h6 1. Choose people you want in the team
.clearfix
= f.label :user_ids, "Peolpe"
.input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).all, :id, :name), { class: "xxlarge", multiple: true })
= f.label :user_ids, "People"
.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
.clearfix
= 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
= f.submit 'Save', class: "btn save-btn"
= link_to "Cancel", team_project_path(@project), class: "btn cancel-btn"
:javascript
$('select#user_ids').chosen();
$('select#project_access').chosen();
= link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn"
- user = member.user
- allow_admin = can? current_user, :admin_project, @project
%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
= image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
%strong= truncate(user.name, lenght: 40)
%br
%div.cgray= user.email
%br
%small.cgray= user.email
%td
%td.span5
.right
- if current_user == user
%span.btn.disabled This is you!
- if @project.owner == user
%span.btn.disabled.success Project Owner
- if user.blocked
%span.btn.disabled.success Owner
- elsif user.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
= 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
Team Members
%small (#{@project.users_projects.count})
......@@ -10,6 +10,4 @@
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
= render partial: "team", locals: {project: @project}
= render partial: "team_members/team", locals: {project: @project}
......@@ -14,7 +14,7 @@
%hr
.back_link
%br
= link_to team_project_path(@project), class: "" do
= link_to project_team_index_path(@project), class: "" do
&larr; To team list
%br
.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 @@
Delete this page
%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:
# backup_keep_time: 604800 # default: 0 (forever) (in seconds)
# 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
......
......@@ -6,7 +6,7 @@ class Settings < Settingslogic
self.web['protocol'] ||= web.https ? "https" : "http"
end
def web_host
def web_host
self.web['host'] ||= 'localhost'
end
......@@ -14,11 +14,11 @@ class Settings < Settingslogic
self.email['from'] ||= ("notify@" + web_host)
end
def url
def url
self['url'] ||= build_url
end
end
def web_port
def web_port
if web.https
web['port'] = 443
else
......@@ -36,7 +36,7 @@ class Settings < Settingslogic
raw_url << web_host
if web_custom_port?
raw_url << ":#{web_port}"
raw_url << ":#{web_port}"
end
raw_url
......@@ -120,6 +120,22 @@ class Settings < Settingslogic
app['backup_keep_time'] || 0
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?
app['disable_gravatar'] || false
end
......
......@@ -204,4 +204,21 @@ Devise.setup do |config|
# manager.intercept_401 = false
# manager.default_strategies(:scope => :user).unshift :some_external_strategy
# 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
# 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
# Optionally, enable Resque here
require 'resque/server'
mount Resque::Server.new, at: '/info/resque', as: 'resque'
mount Resque::Server => '/info/resque', as: 'resque'
# Enable Grack support
mount Grack::Bundle.new({
......@@ -23,14 +23,14 @@ Gitlab::Application.routes.draw do
#
# Help
#
get 'help' => 'help#index'
get 'help/permissions' => 'help#permissions'
get 'help/workflow' => 'help#workflow'
get 'help/api' => 'help#api'
get 'help/web_hooks' => 'help#web_hooks'
get 'help' => 'help#index'
get 'help/permissions' => 'help#permissions'
get 'help/workflow' => 'help#workflow'
get 'help/api' => 'help#api'
get 'help/web_hooks' => 'help#web_hooks'
get 'help/system_hooks' => 'help#system_hooks'
get 'help/markdown' => 'help#markdown'
get 'help/ssh' => 'help#ssh'
get 'help/markdown' => 'help#markdown'
get 'help/ssh' => 'help#ssh'
#
# Admin Area
......@@ -43,19 +43,19 @@ Gitlab::Application.routes.draw do
put :unblock
end
end
resources :projects, :constraints => { :id => /[^\/]+/ } do
resources :projects, constraints: { id: /[^\/]+/ } do
member do
get :team
put :team_update
end
end
resources :team_members, :only => [:edit, :update, :destroy]
resources :hooks, :only => [:index, :create, :destroy] do
resources :team_members, only: [:edit, :update, :destroy]
resources :hooks, only: [:index, :create, :destroy] do
get :test
end
resource :logs
resource :resque, :controller => 'resque'
root :to => "dashboard#index"
resource :logs, only: [:show]
resource :resque, controller: 'resque', only: [:show]
root to: "dashboard#index"
end
get "errors/githost"
......@@ -63,38 +63,39 @@ Gitlab::Application.routes.draw do
#
# Profile Area
#
get "profile/password", :to => "profile#password"
put "profile/password", :to => "profile#password_update"
get "profile/token", :to => "profile#token"
put "profile/reset_private_token", :to => "profile#reset_private_token"
get "profile", :to => "profile#show"
get "profile/design", :to => "profile#design"
put "profile/update", :to => "profile#update"
get "profile/account" => "profile#account"
get "profile/history" => "profile#history"
put "profile/password" => "profile#password_update"
get "profile/token" => "profile#token"
put "profile/reset_private_token" => "profile#reset_private_token"
get "profile" => "profile#show"
get "profile/design" => "profile#design"
put "profile/update" => "profile#update"
resources :keys
#
# Dashboard Area
#
get "dashboard", :to => "dashboard#index"
get "dashboard/issues", :to => "dashboard#issues"
get "dashboard/merge_requests", :to => "dashboard#merge_requests"
get "dashboard" => "dashboard#index"
get "dashboard/issues" => "dashboard#issues"
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
#
resources :projects, :constraints => { :id => /[^\/]+/ }, :except => [:new, :create, :index], :path => "/" do
resources :projects, constraints: { id: /[^\/]+/ }, except: [:new, :create, :index], path: "/" do
member do
get "team"
get "wall"
get "graph"
get "files"
end
resources :wikis, :only => [:show, :edit, :destroy, :create] do
resources :wikis, only: [:show, :edit, :destroy, :create] do
collection do
get :pages
end
......@@ -113,46 +114,45 @@ Gitlab::Application.routes.draw do
end
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
get "switch"
end
member do
get "tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ }
get "logs_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 "blob",
:constraints => {
:id => /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/
constraints: {
id: /[a-zA-Z.0-9\/_\-]+/,
path: /.*/
}
# tree viewer
get "tree/:path" => "refs#tree",
:as => :tree_file,
:constraints => {
:id => /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/
as: :tree_file,
constraints: {
id: /[a-zA-Z.0-9\/_\-]+/,
path: /.*/
}
# tree viewer
get "logs_tree/:path" => "refs#logs_tree",
:as => :logs_file,
:constraints => {
:id => /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/
as: :logs_file,
constraints: {
id: /[a-zA-Z.0-9\/_\-]+/,
path: /.*/
}
# blame
get "blame/:path" => "refs#blame",
:as => :blame_file,
:constraints => {
:id => /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/
as: :blame_file,
constraints: {
id: /[a-zA-Z.0-9\/_\-]+/,
path: /.*/
}
end
end
......@@ -177,7 +177,7 @@ Gitlab::Application.routes.draw do
end
end
resources :hooks, :only => [:index, :create, :destroy] do
resources :hooks, only: [:index, :create, :destroy] do
member do
get :test
end
......@@ -191,9 +191,10 @@ Gitlab::Application.routes.draw do
get :patch
end
end
resources :team, controller: 'team_members', only: [:index]
resources :team_members
resources :milestones
resources :labels, :only => [:index]
resources :labels, only: [:index]
resources :issues do
collection do
......@@ -202,11 +203,12 @@ Gitlab::Application.routes.draw do
get :search
end
end
resources :notes, :only => [:index, :create, :destroy] do
resources :notes, only: [:index, :create, :destroy] do
collection do
post :preview
end
end
end
root :to => "dashboard#index"
root to: "dashboard#index"
end
......@@ -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)
+ [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.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:
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
Get a list of repository branches from a project, sorted by name alphabetically.
......
......@@ -36,10 +36,10 @@
### 3. Run Tests
# All in one
bundle exec gitlab:test
bundle exec rake gitlab:test
# Rspec
bundle exec rake spec
# Cucumber
bundle exec rake cucumber
# Spinach
bundle exec rake spinach
......@@ -152,7 +152,14 @@ and ensure you have followed all of the above steps carefully.
sudo pip install pygments
sudo gem install bundler
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
# 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
# Rename config files
......@@ -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
# 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. **
**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:
## 1. Unicorn
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
## 2. Nginx
......@@ -269,7 +284,6 @@ You can login via web using admin generated with setup:
# of the host serving GitLab.
sudo vim /etc/nginx/sites-enabled/gitlab
# Restart nginx:
/etc/init.d/nginx restart
......
Feature: Dashboard
Background:
Given I signin as a user
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" has push event
And I visit dashboard page
And I visit dashboard page
Scenario: I should see projects list
Then I should see "New Project" link
......@@ -25,4 +25,3 @@ Feature: Dashboard
And user with name "John Doe" left project "Shop"
When I visit dashboard page
Then I should see "John Doe left project Shop" event
Feature: Dashboard Issues
Background:
Given I signin as a user
Background:
Given I sign in as a user
And I have assigned issues
And I visit dashboard issues page
And I visit dashboard issues page
Scenario: I should see issues list
Then I should see issues assigned to me
Feature: Dashboard MR
Background:
Given I signin as a user
Feature: Dashboard Merge Requests
Background:
Given I sign in as a user
And I have authored merge requests
And I visit dashboard merge requests page
And I visit dashboard merge requests page
Scenario: I should see projects list
Then I should see my merge requests
Feature: Dashboard Search
Background:
Given I signin as a user
Background:
Given I sign in as a user
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"
Then I should see "Shop" project link
Feature: Profile
Background:
Given I signin as a user
Background:
Given I sign in as a user
Scenario: I look at my profile
Given I visit profile page
......@@ -12,11 +12,11 @@ Feature: Profile
And I should see new contact info
Scenario: I change my password
Given I visit profile password page
Given I visit profile account page
Then I change my password
And I should be redirected to sign in page
Scenario: I reset my token
Given I visit profile token page
Given I visit profile account page
Then I reset my token
And I should see new token
Feature: SSH Keys
Background:
Given I signin as a user
And I have ssh keys:
| title |
| ssh-rsa Work |
| ssh-rsa Home |
Feature: Profile SSH Keys
Background:
Given I sign in as a user
And I have ssh key "ssh-rsa Work"
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
Scenario: Add new ssh key
......
Feature: Browse branches
Background:
Given I signin as a user
Feature: Project Browse branches
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" has protected branches
Given I visit project branches page
......@@ -16,8 +16,11 @@ Feature: Browse branches
Given I click link "Protected"
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
Background:
Given I signin as a user
Feature: Project Comment commit
Background:
Given I sign in as a user
And I own project "Shop"
Given I visit project commit page
......
Feature: Browse commits
Background:
Given I signin as a user
Feature: Project Browse commits
Background:
Given I sign in as a user
And I own project "Shop"
Given I visit project commits page
......@@ -18,5 +18,4 @@ Feature: Browse commits
Scenario: I compare refs
Given I visit compare refs page
And I fill compare fields with refs
And I see compared refs
And I see compared refs
Feature: Browse tags
Background:
Given I signin as a user
Feature: Project Browse tags
Background:
Given I sign in as a user
And I own project "Shop"
Given I visit project tags page
Scenario: I can see all git tags
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
Should be able to create a new one
Scenario: User create a project
Given I signin as a user
Given I sign in as a user
When I visit new project page
And fill project form with valid data
Then I should see project page
......
Feature: Issues
Feature: Project Issues
Background:
Given I signin as a user
Given I sign in as a user
And I own project "Shop"
And project "Shop" have "Release 0.4" open issue
And project "Shop" have "Release 0.3" closed issue
......@@ -79,4 +79,3 @@ Feature: Issues
When I select first assignee from "Shop" project
And I click link "New Issue"
Then I should see first assignee from "Shop" as selected assignee
Feature: Labels
Feature: Project Labels
Background:
Given I signin as a user
Given I sign in as a user
And I own project "Shop"
And project "Shop" have issues tags:
| name |
| bug |
| feature |
Given I visit project "Shop" labels page
And project "Shop" have issues tags: "bug", "feature"
Given I visit project "Shop" labels page
Scenario: I should see active milestones
Then I should see label "bug"
......
Feature: Milestones
Feature: Project Milestones
Background:
Given I signin as a user
Given I sign in as a user
And I own project "Shop"
And project "Shop" has milestone "v2.2"
Given I visit project "Shop" milestones page
Given I visit project "Shop" milestones page
Scenario: I should see active milestones
Then I should see milestone "v2.2"
......
Feature: Merge Requests
Feature: Project Merge Requests
Background:
Given I signin as a user
Given I sign in as a user
And I own project "Shop"
And project "Shop" have "Bug NS-04" open merge request
And project "Shop" have "Feature NS-03" closed merge request
And I visit project "Shop" merge requests page
And I visit project "Shop" merge requests page
Scenario: I should see open merge requests
Then I should see "Bug NS-04" in merge requests
......
@javascript
Feature: Project Network Graph
Background:
Given I signin as a user
Given I sign in as a user
And I own project "Shop"
And I visit project "Shop" network page
@javascript
Scenario: I should see project network
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
Background:
Given I signin as a user
Feature: Project Browse files
Background:
Given I sign in as a user
And I own project "Shop"
Given I visit project source page
......@@ -12,12 +12,10 @@ Feature: Browse git repo
Then I should see files from repository for "8470d70"
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
Scenario: I browse raw file
Given I visit blob file from repo
And I click on raw button
Given I visit blob file from repo
And I click link "raw"
Then I should see raw file content
Feature: Browse git repo
Background:
Given I signin as a user
Feature: Project Browse git repo
Background:
Given I sign in as a user
And I own project "Shop"
Given I visit project source page
Scenario: I blame file
Given I click on file from repo
Given I click on "Gemfile" file in repo
And I click blame button
Then I should see git file blame
Then I should see git file blame
Feature: Project Team management
Background:
Given I signin as a user
Background:
Given I sign in as a user
And I own project "Shop"
And gitlab user "Mike"
And gitlab user "Sam"
And gitlab user "Mike"
And gitlab user "Sam"
And "Sam" is "Shop" developer
And I visit project "Shop" team page
And I visit project "Shop" team page
Scenario: See all team members
Then I should be able to see myself in team
......@@ -20,7 +20,7 @@ Feature: Project Team management
Scenario: Update user access
Given I should see "Sam" in team list as "Developer"
And I change "Sam" role to "Reporter"
Then I visit project "Shop" team page
Then I visit project "Shop" team page
And I should see "Sam" in team list as "Reporter"
Scenario: View team member profile
......@@ -30,6 +30,5 @@ Feature: Project Team management
Scenario: Cancel team member
Given I click link "Sam"
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
@javascript
Feature: Project Wall
In order to use Project Wall
A user
Should be able to read & write messages
A user should be able to read and write messages
Background:
Given I signin as a user
Given I sign in as a user
And I own project "Shop"
And I visit project "Shop" wall page
And I visit project "Shop" wall page
@javascript
Scenario: Write comment
Given I write new comment "my special test message"
Then I should see project wall note "my special test message"
Then I visit project "Shop" wall page
Then I visit project "Shop" wall page
And I should see project wall note "my special test message"
Feature: Wiki
Background:
Given I signin as a user
Feature: Project Wiki
Background:
Given I sign in as a user
And I own project "Shop"
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']
require 'simplecov'
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'
ENV['RAILS_ENV'] = 'test'
require './config/environment'
Capybara.default_selector = :css
Capybara.javascript_driver = :webkit
require 'rspec'
require 'database_cleaner'
require 'spinach/capybara'
# By default, any exception happening in your Rails application will bubble up
# to Cucumber so that your scenario will fail. This is a different from how
# 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."
%w(gitolite_stub stubbed_repository valid_commit).each do |f|
require Rails.root.join('spec', 'support', f)
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
headless.start
WebMock.allow_net_connect!
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!
end
World(FactoryGirl::Syntax::Methods)
......@@ -17,5 +17,6 @@ module Gitlab
mount Projects
mount Issues
mount Milestones
mount Keys
end
end
......@@ -9,6 +9,10 @@ module Gitlab
expose :id, :email, :name, :blocked, :created_at
end
class Hook < Grape::Entity
expose :id, :url
end
class Project < Grape::Entity
expose :id, :code, :name, :description, :path, :default_branch
expose :owner, using: Entities::UserBasic
......@@ -44,5 +48,11 @@ module Gitlab
expose :assignee, :author, using: Entities::UserBasic
expose :closed, :updated_at, :created_at
end
class Key < Grape::Entity
expose :id,
:title,
:key
end
end
end
......@@ -8,7 +8,7 @@ module Gitlab
if @project ||= current_user.projects.find_by_id(params[:id]) ||
current_user.projects.find_by_code(params[:id])
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
@project
......@@ -19,15 +19,48 @@ module Gitlab
end
def authenticate!
error!({'message' => '401 Unauthorized'}, 401) unless current_user
unauthorized! unless current_user
end
def authorize! action, subject
unless abilities.allowed?(current_user, action, subject)
error!({'message' => '403 Forbidden'}, 403)
forbidden!
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
def abilities
......
......@@ -48,19 +48,14 @@ module Gitlab
# Example Request:
# POST /projects/:id/issues
post ":id/issues" do
@issue = user_project.issues.new(
title: params[:title],
description: params[:description],
assignee_id: params[:assignee_id],
milestone_id: params[:milestone_id],
label_list: params[:labels]
)
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
attrs[:label_list] = params[:labels] if params[:labels].present?
@issue = user_project.issues.new attrs
@issue.author = current_user
if @issue.save
present @issue, with: Entities::Issue
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
......@@ -81,19 +76,12 @@ module Gitlab
@issue = user_project.issues.find(params[:issue_id])
authorize! :modify_issue, @issue
parameters = {
title: (params[:title] || @issue.title),
description: (params[:description] || @issue.description),
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)
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :closed]
attrs[:label_list] = params[:labels] if params[:labels].present?
if @issue.update_attributes attrs
present @issue, with: Entities::Issue
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
......@@ -105,7 +93,7 @@ module Gitlab
# Example Request:
# DELETE /projects/:id/issues/:issue_id
delete ":id/issues/:issue_id" do
error!({'message' => 'method not allowed'}, 405)
not_allowed!
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
# Example Request:
# POST /projects/:id/milestones
post ":id/milestones" do
@milestone = user_project.milestones.new(
title: params[:title],
description: params[:description],
due_date: params[:due_date]
)
attrs = attributes_for_keys [:title, :description, :due_date]
@milestone = user_project.milestones.new attrs
if @milestone.save
present @milestone, with: Entities::Milestone
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
......@@ -64,17 +60,11 @@ module Gitlab
authorize! :admin_milestone, user_project
@milestone = user_project.milestones.find(params[:milestone_id])
parameters = {
title: (params[:title] || @milestone.title),
description: (params[:description] || @milestone.description),
due_date: (params[:due_date] || @milestone.due_date),
closed: (params[:closed] || @milestone.closed)
}
if @milestone.update_attributes(parameters)
attrs = attributes_for_keys [:title, :description, :due_date, :closed]
if @milestone.update_attributes attrs
present @milestone, with: Entities::Milestone
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
end
......
......@@ -40,17 +40,20 @@ module Gitlab
post do
params[:code] ||= params[:name]
params[:path] ||= params[:name]
project_attrs = {}
params.each_pair do |k ,v|
if Project.attribute_names.include? k
project_attrs[k] = v
end
end
@project = Project.create_by_user(project_attrs, current_user)
attrs = attributes_for_keys [:code,
:path,
:name,
:description,
:default_branch,
:issues_enabled,
:wall_enabled,
:merge_requests_enabled,
:wiki_enabled]
@project = Project.create_by_user(attrs, current_user)
if @project.saved?
present @project, with: Entities::Project
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
......@@ -106,6 +109,49 @@ module Gitlab
nil
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
#
# Parameters:
......@@ -161,18 +207,16 @@ module Gitlab
# Example Request:
# POST /projects/:id/snippets
post ":id/snippets" do
@snippet = user_project.snippets.new(
title: params[:title],
file_name: params[:file_name],
expires_at: params[:lifetime],
content: params[:code]
)
attrs = attributes_for_keys [:title, :file_name]
attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
attrs[:content] = params[:code] if params[:code].present?
@snippet = user_project.snippets.new attrs
@snippet.author = current_user
if @snippet.save
present @snippet, with: Entities::ProjectSnippet
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
......@@ -191,17 +235,14 @@ module Gitlab
@snippet = user_project.snippets.find(params[:snippet_id])
authorize! :modify_snippet, @snippet
parameters = {
title: (params[:title] || @snippet.title),
file_name: (params[:file_name] || @snippet.file_name),
expires_at: (params[:lifetime] || @snippet.expires_at),
content: (params[:code] || @snippet.content)
}
attrs = attributes_for_keys [:title, :file_name]
attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
attrs[:content] = params[:code] if params[:code].present?
if @snippet.update_attributes(parameters)
if @snippet.update_attributes attrs
present @snippet, with: Entities::ProjectSnippet
else
error!({'message' => '404 Not found'}, 404)
not_found!
end
end
......@@ -244,10 +285,10 @@ module Gitlab
ref = params[:sha]
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]
error!('404 File Not Found', 404) unless tree.try(:tree)
not_found! "File" unless tree.try(:tree)
if tree.text?
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
end
end
rescue PullError => ex
Gitlab::Logger.error("Pull error -> " + ex.message)
log("Pull error -> " + ex.message)
raise Gitolite::AccessDenied, ex.message
rescue PushError => ex
Gitlab::Logger.error("Push error -> " + " " + ex.message)
log("Push error -> " + " " + ex.message)
raise Gitolite::AccessDenied, ex.message
rescue Exception => ex
Gitlab::Logger.error(ex.class.name + " " + ex.message)
log(ex.class.name + " " + ex.message)
raise Gitolite::AccessDenied.new("gitolite timeout")
end
def log message
Gitlab::GitLogger.error(message)
end
def destroy_project(project)
FileUtils.rm_rf(project.path_to_repo)
conf.rm_repo(project.path)
......@@ -148,7 +152,7 @@ module Gitlab
# Enable access to all repos for gitolite admin.
# We use it for accept merge request feature
def admin_all_repo
owner_name = Gitlab.settings.gitolite_admin_key
owner_name = Gitlab.config.gitolite_admin_key
# @ALL repos premission for gitolite owner
repo_name = "@all"
......
......@@ -12,21 +12,22 @@ module Grack
# Pass Gitolite update hook
ENV['GL_BYPASS_UPDATE_HOOK'] = "true"
# Need this patch because the rails mount
@env['PATH_INFO'] = @env['REQUEST_PATH']
# Need this patch due to the rails mount
@env['PATH_INFO'] = @request.path
@env['SCRIPT_NAME'] = ""
# 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)
end
# Git upload and receive
if @env['REQUEST_METHOD'] == 'GET'
if @request.get?
true
elsif @env['REQUEST_METHOD'] == 'POST'
if @env['REQUEST_URI'].end_with?('git-upload-pack')
elsif @request.post?
if @request.path_info.end_with?('git-upload-pack')
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)
project.master_access_for?(user)
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
attr_accessor :time, :space
attr_accessor :refs
include ActionView::Helpers::SanitizeHelper
def self.to_graph(project)
@repo = project.repo
commits = Grit::Commit.find_all(@repo, nil, {max_count: 650})
......@@ -164,7 +166,7 @@ module Gitlab
h[:refs] = refs.collect{|r|r.name}.join(" ") unless refs.nil?
h[:id] = sha
h[:date] = date
h[:message] = Gitlab::Encode.utf8(message)
h[:message] = sanitize(Gitlab::Encode.utf8(message))
h[:login] = author.email
h
end
......
......@@ -9,17 +9,13 @@ module Gitlab
end
def self.read_latest
path = Rails.root.join("log/githost.log")
path = Rails.root.join("log", file_name)
self.build unless File.exist?(path)
logs = File.read(path).split("\n")
end
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
......@@ -26,13 +26,13 @@ module Gitlab
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
module Markdown
REFERENCE_PATTERN = %r{
([^\w&;])? # Prefix (1)
(\W)? # Prefix (1)
( # Reference (2)
@([\w\._]+) # User name (3)
|[#!$](\d+) # Issue/MR/Snippet ID (4)
|([\h]{6,40}) # Commit ID (5)
)
([^\w&;])? # Suffix (6)
(\W)? # Suffix (6)
}x.freeze
EMOJI_PATTERN = %r{(:(\S+):)}.freeze
......@@ -48,8 +48,10 @@ module Gitlab
def gfm(text, html_options = {})
return text if text.nil?
# prevents the string supplied through the _text_ argument to be altered
text = text.dup
# Duplicate the string so we don't alter the original, then call to_str
# 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
......@@ -84,6 +86,13 @@ module Gitlab
#
# Returns parsed text
def parse(text)
parse_references(text) if @project
parse_emoji(text)
text
end
def parse_references(text)
# parse reference links
text.gsub!(REFERENCE_PATTERN) do |match|
prefix = $1 || ''
......@@ -91,13 +100,18 @@ module Gitlab
identifier = $3 || $4 || $5
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
else
match
end
end if @project
end
end
def parse_emoji(text)
# parse emoji
text.gsub!(EMOJI_PATTERN) do |match|
if valid_emoji?($2)
......@@ -106,8 +120,6 @@ module Gitlab
match
end
end
text
end
# 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
desc "GITLAB | Run both cucumber & rspec"
task :test => ['cucumber', 'spec']
desc "GITLAB | Run both spinach and rspec"
task :test => ['spinach', 'spec']
end
task :travis do
["cucumber", "rspec spec"].each do |cmd|
["spinach", "rspec spec"].each do |cmd|
puts "Starting to run #{cmd}..."
system("export DISPLAY=:99.0 && bundle exec #{cmd}")
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
end
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."
gfm(actual).should == expected
end
......@@ -291,11 +292,18 @@ describe GitlabMarkdownHelper do
actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
actual.should have_selector 'a.gfm.gfm-commit.foo'
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
describe "#markdown" 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
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
......@@ -14,12 +14,12 @@ describe Event do
it { should respond_to(:commits) }
end
describe "Push event" do
before do
describe "Push event" do
before do
project = Factory :project
@user = project.owner
data = {
data = {
before: "0000000000000000000000000000000000000000",
after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e",
ref: "refs/heads/master",
......@@ -50,25 +50,24 @@ describe Event do
it { @event.author.should == @user }
end
describe "Joined project team" do
let(:project) {Factory.create :project}
let(:new_user) {Factory.create :user}
it "should create event" do
UsersProject.observers.enable :users_project_observer
expect{
UsersProject.bulk_import(project, [new_user.id], UsersProject::DEVELOPER)
}.to change{Event.count}.by(1)
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
it "should create event" do
observer.after_create user_project
end
end
end
describe "Left project team" do
let(:project) {Factory.create :project}
let(:new_user) {Factory.create :user}
it "should create event" do
UsersProject.bulk_import(project, [new_user.id], UsersProject::DEVELOPER)
UsersProject.observers.enable :users_project_observer
expect{
UsersProject.bulk_delete(project, [new_user.id])
}.to change{Event.count}.by(1)
describe "Left project team" do
it "should create event" do
observer.after_destroy user_project
end
end
end
end
......@@ -12,7 +12,7 @@ describe Issue do
describe 'modules' do
it { should include_module(IssueCommonality) }
it { should include_module(Upvote) }
it { should include_module(Votes) }
end
subject { Factory.create(:issue) }
......
......@@ -8,6 +8,6 @@ describe MergeRequest do
describe 'modules' do
it { should include_module(IssueCommonality) }
it { should include_module(Upvote) }
it { should include_module(Votes) }
end
end
......@@ -24,6 +24,13 @@ describe Note do
it "recognizes a neutral note" do
note = Factory(:note, note: "This is not a +1 note")
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
it "recognizes a +1 note" do
......@@ -31,19 +38,19 @@ describe Note do
note.should be_upvote
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
note = build(:note, note: ":+1: for this")
note.should be_upvote
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
it "recognizes a -1 note" do
note = Factory(:note, note: "-1 for this")
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
......
......@@ -13,7 +13,7 @@ describe UserObserver do
end
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 }
it 'sends an email' do
......@@ -22,5 +22,10 @@ describe UserObserver do
subject.after_create(user)
end
it 'trigger logger' do
Gitlab::AppLogger.should_receive(:info)
subject.after_create(user)
end
end
end
require 'spec_helper'
describe UsersProjectObserver do
# let(:users_project) { stub.as_null_object }
let(:user) { Factory.create :user }
let(:project) { Factory.create(:project,
code: "Fuu",
......@@ -14,21 +15,23 @@ describe UsersProjectObserver do
it "should called when UsersProject created" do
subject.should_receive(:after_commit).once
UsersProject.observers.enable :users_project_observer do
Factory.create(:users_project,
project: project,
user: user)
create(:users_project)
end
end
it "should send email to user" do
Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true))
subject.after_commit(users_project)
Event.stub(:create => true)
end
it "should create new event" do
Event.should_receive(:create).with(
project_id: users_project.project.id,
action: Event::Joined,
project_id: users_project.project.id,
action: Event::Joined,
author_id: users_project.user.id
)
subject.after_create(users_project)
end
end
......@@ -37,9 +40,10 @@ describe UsersProjectObserver do
it "should called when UsersProject updated" do
subject.should_receive(:after_commit).once
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
it "should send email to user" do
Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true))
subject.after_commit(users_project)
......@@ -51,20 +55,20 @@ describe UsersProjectObserver do
end
end
end
describe "#after_destroy" do
it "should called when UsersProject destroyed" do
subject.should_receive(:after_destroy)
UsersProject.observers.enable :users_project_observer do
UsersProject.bulk_delete(
users_project.project,
[users_project.user.id]
)
create(:users_project).destroy
end
end
it "should create new event" do
Event.should_receive(:create).with(
project_id: users_project.project.id,
action: Event::Left,
project_id: users_project.project.id,
action: Event::Left,
author_id: users_project.user.id
)
subject.after_destroy(users_project)
......
......@@ -9,12 +9,14 @@ describe Gitlab::API do
before { project.add_access(user, :read) }
describe "GET /issues" do
it "should return authentication error" do
get api("/issues")
response.status.should == 401
context "when unauthenticated" do
it "should return authentication error" do
get api("/issues")
response.status.should == 401
end
end
describe "authenticated GET /issues" do
context "when authenticated" do
it "should return an array of issues" do
get api("/issues", user)
response.status.should == 200
......
......@@ -6,6 +6,7 @@ describe Gitlab::API do
let(:user) { Factory :user }
let(:user2) { 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!(:snippet) { Factory :snippet, author: user, project: project, title: 'example' }
let!(:users_project) { Factory :users_project, user: user, project: project, project_access: UsersProject::MASTER }
......@@ -13,12 +14,14 @@ describe Gitlab::API do
before { project.add_access(user, :read) }
describe "GET /projects" do
it "should return authentication error" do
get api("/projects")
response.status.should == 401
context "when unauthenticated" do
it "should return authentication error" do
get api("/projects")
response.status.should == 401
end
end
describe "authenticated GET /projects" do
context "when authenticated" do
it "should return an array of projects" do
get api("/projects", user)
response.status.should == 200
......@@ -85,7 +88,7 @@ describe Gitlab::API do
it "should return a 404 error if not found" do
get api("/projects/42", user)
response.status.should == 404
json_response['message'].should == '404 Not found'
json_response['message'].should == '404 Not Found'
end
end
......@@ -147,6 +150,36 @@ describe Gitlab::API do
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
it "should return an array of project tags" do
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
let(:user) { Factory :user }
describe "GET /users" do
it "should return authentication error" do
get api("/users")
response.status.should == 401
context "when unauthenticated" do
it "should return authentication error" do
get api("/users")
response.status.should == 401
end
end
describe "authenticated GET /users" do
context "when authenticated" do
it "should return an array of users" do
get api("/users", user)
response.status.should == 200
......
require 'spec_helper'
describe "User Issues Dashboard" do
describe "Dashboard Issues Feed" 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
@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
describe "atom feed" 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.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("entry summary", text: @issue1.title)
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("title", text: "#{user.name} issues")
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("author email", text: issue2.author_email)
page.body.should have_selector("entry summary", text: issue2.title)
end
end
end
......
require 'spec_helper'
describe "User Dashboard" do
before { login_as :user }
describe "Dashboard Feed" do
describe "GET /" do
before do
@project = Factory :project, owner: @user
@project.add_access(@user, :read)
visit dashboard_path
end
let!(:user) { Factory :user }
it "should render projects atom feed via private token" do
logout
visit dashboard_path(:atom, private_token: @user.private_token)
page.body.should have_selector("feed title")
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")
end
end
it "should not render projects page via private token" do
logout
visit dashboard_path(private_token: @user.private_token)
current_path.should == new_user_session_path
context "projects page via private token" do
it "should redirect to login page" do
visit dashboard_path(private_token: user.private_token)
current_path.should == new_user_session_path
end
end
end
end
require 'spec_helper'
describe "Issues" do
let(:project) { Factory :project }
before do
login_as :user
project.add_access(@user, :read, :write)
end
describe "Issues Feed" do
describe "GET /issues" do
before do
@issue = Factory :issue,
author: @user,
assignee: @user,
project: project
visit project_issues_path(project)
end
it "should render atom feed" do
visit project_issues_path(project, :atom)
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("author email", text: @issue.author_email)
page.body.should have_selector("entry summary", text: @issue.title)
let!(:user) { Factory :user }
let!(:project) { Factory :project, owner: user }
let!(:issue) { Factory :issue, author: user, project: project }
before { project.add_access(user, :read, :write) }
context "when authenticated" do
it "should render atom feed" do
login_with user
visit project_issues_path(project, :atom)
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("author email", text: issue.author_email)
page.body.should have_selector("entry summary", text: issue.title)
end
end
it "should render atom feed via private token" do
logout
visit project_issues_path(project, :atom, private_token: @user.private_token)
context "when authenticated via private token" do
it "should render atom feed" do
visit project_issues_path(project, :atom, private_token: user.private_token)
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("author email", text: @issue.author_email)
page.body.should have_selector("entry summary", text: @issue.title)
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("author email", text: issue.author_email)
page.body.should have_selector("entry summary", text: issue.title)
end
end
end
end
......@@ -25,6 +25,7 @@ describe "Gitlab Flavored Markdown" do
@tag_name = "gfm-test-tag"
r.git.native(:tag, {}, @tag_name, commit.id)
end
after do
# delete test branch and tag
project.repo.git.native(:branch, {D: true}, @branch_name)
......
......@@ -28,8 +28,8 @@ describe "Users Security" do
it { should be_denied_for :visitor }
end
describe "GET /profile/password" do
subject { profile_password_path }
describe "GET /profile/account" do
subject { profile_account_path }
it { should be_allowed_for @u1 }
it { should be_allowed_for :admin }
......
......@@ -70,7 +70,7 @@ describe "Application access" do
end
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 @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
end
def stub_gitolite_admin
gitolite_repo = mock(
clean_permissions: true,
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_admin = double('Gitolite::GitoliteAdmin')
gitolite_admin.as_null_object
Gitolite::GitoliteAdmin.stub(new: gitolite_admin)
end
def stub_gitlab_gitolite
gitolite_config = double('Gitlab::GitoliteConfig')
gitolite_config.stub(
apply: ->() { yield(self) },
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,
)
gitolite_config.stub(apply: ->() { yield(self) })
gitolite_config.as_null_object
Gitlab::GitoliteConfig.stub(new: gitolite_config)
end
......
......@@ -73,11 +73,7 @@ module Shoulda::Matchers::ActiveModel
class EnsureLengthOfMatcher
# Shortcut for is_at_least and is_at_most
def is_within(range)
if range.exclude_end?
is_at_least(range.first) && is_at_most(range.last - 1)
else
is_at_least(range.first) && is_at_most(range.last)
end
is_at_least(range.min) && is_at_most(range.max)
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