Merge branch 'master' into project_hooks_api
This commit is contained in:
commit
2e34a6d3c4
58 changed files with 468 additions and 335 deletions
22
Gemfile
22
Gemfile
|
@ -1,5 +1,13 @@
|
|||
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
|
||||
|
@ -44,7 +52,8 @@ gem "ffaker"
|
|||
gem "seed-fu"
|
||||
|
||||
# Markdown to HTML
|
||||
gem "redcarpet", "~> 2.1.1"
|
||||
gem "redcarpet", "~> 2.1.1"
|
||||
gem "github-markup", "~> 0.7.4"
|
||||
|
||||
# Servers
|
||||
gem "thin"
|
||||
|
@ -101,13 +110,20 @@ group :development, :test do
|
|||
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-cucumber'
|
||||
|
||||
# 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
|
||||
|
|
27
Gemfile.lock
27
Gemfile.lock
|
@ -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)
|
||||
|
@ -178,6 +173,7 @@ GEM
|
|||
gherkin (2.11.0)
|
||||
json (>= 1.4.6)
|
||||
git (1.2.5)
|
||||
github-markup (0.7.4)
|
||||
gitlab_meta (2.9)
|
||||
grape (0.2.1)
|
||||
hashie (~> 1.2)
|
||||
|
@ -185,6 +181,15 @@ GEM
|
|||
multi_xml
|
||||
rack
|
||||
rack-mount
|
||||
growl (1.0.3)
|
||||
guard (1.3.2)
|
||||
listen (>= 0.4.2)
|
||||
thor (>= 0.14.6)
|
||||
guard-cucumber (1.2.0)
|
||||
cucumber (>= 1.2.0)
|
||||
guard (>= 1.1.0)
|
||||
guard-rspec (1.2.1)
|
||||
guard (>= 1.1)
|
||||
haml (3.1.6)
|
||||
haml-rails (0.3.4)
|
||||
actionpack (~> 3.0)
|
||||
|
@ -218,6 +223,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)
|
||||
|
@ -273,6 +279,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)
|
||||
|
@ -376,8 +385,6 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
acts-as-taggable-on (= 2.3.1)
|
||||
annotate!
|
||||
autotest
|
||||
autotest-rails
|
||||
awesome_print
|
||||
bootstrap-sass (= 2.0.4)
|
||||
capybara
|
||||
|
@ -396,11 +403,15 @@ DEPENDENCIES
|
|||
ffaker
|
||||
foreman
|
||||
git
|
||||
github-markup (~> 0.7.4)
|
||||
gitlab_meta (= 2.9)
|
||||
gitolite!
|
||||
grack!
|
||||
grape (~> 0.2.1)
|
||||
grit!
|
||||
growl
|
||||
guard-cucumber
|
||||
guard-rspec
|
||||
haml-rails
|
||||
headless
|
||||
httparty
|
||||
|
@ -418,6 +429,8 @@ DEPENDENCIES
|
|||
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
|
||||
|
|
30
Guardfile
Normal file
30
Guardfile
Normal file
|
@ -0,0 +1,30 @@
|
|||
# A sample Guardfile
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
guard 'rspec', :version => 2 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" }
|
||||
|
||||
# Turnip features and steps
|
||||
watch(%r{^spec/acceptance/(.+)\.feature$})
|
||||
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
||||
end
|
||||
|
||||
|
||||
guard 'cucumber' do
|
||||
watch(%r{^features/.+\.feature$})
|
||||
watch(%r{^features/support/.+$}) { 'features' }
|
||||
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
|
||||
end
|
|
@ -5,7 +5,7 @@ function switchToNewIssue(form){
|
|||
$('select#issue_milestone_id').chosen();
|
||||
$("#new_issue_dialog").show("fade", { direction: "right" }, 150);
|
||||
$('.top-tabs .add_new').hide();
|
||||
disableButtonIfEmtpyField("#issue_title", ".save-btn");
|
||||
disableButtonIfEmptyField("#issue_title", ".save-btn");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ function switchToEditIssue(form){
|
|||
$('select#issue_milestone_id').chosen();
|
||||
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
|
||||
$('.add_new').hide();
|
||||
disableButtonIfEmtpyField("#issue_title", ".save-btn");
|
||||
disableButtonIfEmptyField("#issue_title", ".save-btn");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
$(document).ready(function(){
|
||||
|
||||
$(".one_click_select").live("click", function(){
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
|
||||
var buttons = $('[type="submit"]', this);
|
||||
switch( e.type ){
|
||||
case 'ajax:beforeSend':
|
||||
case 'submit':
|
||||
buttons.attr('disabled', 'disabled');
|
||||
break;
|
||||
case ' ajax:complete':
|
||||
default:
|
||||
buttons.removeAttr('disabled');
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
$(".account-box").mouseenter(showMenu);
|
||||
$(".account-box").mouseleave(resetMenu);
|
||||
|
||||
$("#projects-list .project").live('click', function(e){
|
||||
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
|
||||
location.href = $(this).attr("url");
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Focus search field by pressing 's' key
|
||||
*/
|
||||
$(document).keypress(function(e) {
|
||||
if( $(e.target).is(":input") ) return;
|
||||
switch(e.which) {
|
||||
case 115: focusSearch();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Commit show suppressed diff
|
||||
*
|
||||
*/
|
||||
$(".supp_diff_link").bind("click", function() {
|
||||
showDiff(this);
|
||||
});
|
||||
|
||||
/**
|
||||
* Note markdown preview
|
||||
*
|
||||
*/
|
||||
$(document).on('click', '#preview-link', function(e) {
|
||||
$('#preview-note').text('Loading...');
|
||||
|
||||
var previewLinkText = ($(this).text() == 'Preview' ? 'Edit' : 'Preview');
|
||||
$(this).text(previewLinkText);
|
||||
|
||||
var note = $('#note_note').val();
|
||||
if (note.trim().length === 0) { note = 'Nothing to preview'; }
|
||||
$.post($(this).attr('href'), {note: note}, function(data) {
|
||||
$('#preview-note').html(data);
|
||||
});
|
||||
|
||||
$('#preview-note, #note_note').toggle();
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
|
||||
function focusSearch() {
|
||||
$("#search").focus();
|
||||
}
|
||||
|
||||
function updatePage(data){
|
||||
$.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
|
||||
}
|
||||
|
||||
function showMenu() {
|
||||
$(this).toggleClass('hover');
|
||||
}
|
||||
|
||||
function resetMenu() {
|
||||
$(this).removeClass("hover");
|
||||
}
|
||||
|
||||
function slugify(text) {
|
||||
return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
|
||||
}
|
||||
|
||||
function showDiff(link) {
|
||||
$(link).next('table').show();
|
||||
$(link).remove();
|
||||
}
|
||||
|
||||
(function($){
|
||||
var _chosen = $.fn.chosen;
|
||||
$.fn.extend({
|
||||
chosen: function(options) {
|
||||
var default_options = {'search_contains' : 'true'};
|
||||
$.extend(default_options, options);
|
||||
return _chosen.apply(this, [default_options]);
|
||||
}})
|
||||
})(jQuery);
|
||||
|
||||
|
||||
function ajaxGet(url) {
|
||||
$.ajax({type: "GET", url: url, dataType: "script"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable button if text field is empty
|
||||
*/
|
||||
function disableButtonIfEmtpyField(field_selector, button_selector) {
|
||||
field = $(field_selector);
|
||||
if(field.val() == "") {
|
||||
field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled");
|
||||
}
|
||||
|
||||
field.on('keyup', function(){
|
||||
var field = $(this);
|
||||
var closest_submit = field.closest("form").find(button_selector);
|
||||
if(field.val() == "") {
|
||||
closest_submit.attr("disabled", "disabled").addClass("disabled");
|
||||
} else {
|
||||
closest_submit.removeAttr("disabled").removeClass("disabled");
|
||||
}
|
||||
})
|
||||
}
|
92
app/assets/javascripts/main.js.coffee
Normal file
92
app/assets/javascripts/main.js.coffee
Normal file
|
@ -0,0 +1,92 @@
|
|||
window.updatePage = (data) ->
|
||||
$.ajax({type: "GET", url: location.href, data: data, dataType: "script"})
|
||||
|
||||
window.slugify = (text) ->
|
||||
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
|
||||
|
||||
window.ajaxGet = (url) ->
|
||||
$.ajax({type: "GET", url: url, dataType: "script"})
|
||||
|
||||
# Disable button if text field is empty
|
||||
window.disableButtonIfEmptyField = (field_selector, button_selector) ->
|
||||
field = $(field_selector)
|
||||
closest_submit = field.closest("form").find(button_selector)
|
||||
|
||||
closest_submit.disable() if field.val() is ""
|
||||
|
||||
field.on "keyup", ->
|
||||
if $(this).val() is ""
|
||||
closest_submit.disable()
|
||||
else
|
||||
closest_submit.enable()
|
||||
|
||||
$ ->
|
||||
# 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)
|
||||
|
||||
switch e.type
|
||||
when 'ajax:beforeSend', 'submit'
|
||||
buttons.disable()
|
||||
else
|
||||
buttons.enable()
|
||||
|
||||
# Show/Hide the profile menu when hovering the account box
|
||||
$('.account-box').hover -> $(this).toggleClass('hover')
|
||||
|
||||
# Focus search field by pressing 's' key
|
||||
$(document).keypress (e) ->
|
||||
# Don't do anything if typing in an input
|
||||
return if $(e.target).is(":input")
|
||||
|
||||
switch e.which
|
||||
when 115
|
||||
$("#search").focus()
|
||||
e.preventDefault()
|
||||
|
||||
# Commit show suppressed diff
|
||||
$(".supp_diff_link").bind "click", ->
|
||||
$(this).next('table').show()
|
||||
$(this).remove()
|
||||
|
||||
# Note markdown preview
|
||||
$(document).on 'click', '#preview-link', (e) ->
|
||||
$('#preview-note').text('Loading...')
|
||||
|
||||
previewLinkText = if $(this).text() == 'Preview' then 'Edit' else 'Preview'
|
||||
$(this).text(previewLinkText)
|
||||
|
||||
note = $('#note_note').val()
|
||||
|
||||
if note.trim().length == 0
|
||||
$('#preview-note').text("Nothing to preview.")
|
||||
else
|
||||
$.post $(this).attr('href'), {note: note}, (data) ->
|
||||
$('#preview-note').html(data)
|
||||
|
||||
$('#preview-note, #note_note').toggle()
|
||||
e.preventDefault()
|
||||
false
|
||||
|
||||
(($) ->
|
||||
_chosen = $.fn.chosen
|
||||
$.fn.extend chosen: (options) ->
|
||||
default_options = search_contains: "true"
|
||||
$.extend default_options, options
|
||||
_chosen.apply this, [default_options]
|
||||
|
||||
# Disable an element and add the 'disabled' Bootstrap class
|
||||
$.fn.extend disable: ->
|
||||
$(this).attr('disabled', 'disabled').addClass('disabled')
|
||||
|
||||
# Enable an element and remove the 'disabled' Bootstrap class
|
||||
$.fn.extend enable: ->
|
||||
$(this).removeAttr('disabled').removeClass('disabled')
|
||||
|
||||
)(jQuery)
|
|
@ -25,14 +25,14 @@ var NoteList = {
|
|||
$(this).closest('li').fadeOut(); });
|
||||
|
||||
$(".note-form-holder").live("ajax:before", function(){
|
||||
$(".submit_note").attr("disabled", "disabled");
|
||||
$(".submit_note").disable()
|
||||
})
|
||||
|
||||
$(".note-form-holder").live("ajax:complete", function(){
|
||||
$(".submit_note").removeAttr("disabled");
|
||||
$(".submit_note").enable()
|
||||
})
|
||||
|
||||
disableButtonIfEmtpyField(".note-text", ".submit_note");
|
||||
disableButtonIfEmptyField(".note-text", ".submit_note");
|
||||
|
||||
$(".note-text").live("focus", function(){
|
||||
$(this).css("height", "80px");
|
||||
|
@ -177,6 +177,6 @@ var PerLineNotes = {
|
|||
form.show();
|
||||
return false;
|
||||
});
|
||||
disableButtonIfEmtpyField(".line-note-text", ".submit_inline_note");
|
||||
disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ window.Projects = ->
|
|||
$('.save-project-loader').show()
|
||||
|
||||
$('form #project_default_branch').chosen()
|
||||
disableButtonIfEmtpyField '#project_name', '.project-submit'
|
||||
disableButtonIfEmptyField '#project_name', '.project-submit'
|
||||
|
||||
# Git clone panel switcher
|
||||
$ ->
|
||||
|
|
|
@ -179,6 +179,15 @@ span.update-author {
|
|||
&.merged {
|
||||
background-color: #2A2;
|
||||
}
|
||||
|
||||
&.joined {
|
||||
background-color: #1ca9dd;
|
||||
}
|
||||
|
||||
&.left {
|
||||
background-color: #888;
|
||||
float:none;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'github/markup'
|
||||
|
||||
class RefsController < ApplicationController
|
||||
include Gitlab::Encode
|
||||
before_filter :project
|
||||
|
|
|
@ -8,7 +8,9 @@ class EventDecorator < ApplicationDecorator
|
|||
"#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title
|
||||
elsif self.push?
|
||||
"#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name
|
||||
else
|
||||
elsif self.membership_changed?
|
||||
"#{self.author_name} #{self.action_name} #{self.project.name}"
|
||||
else
|
||||
""
|
||||
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
|
||||
|
||||
|
|
|
@ -24,4 +24,14 @@ module TreeHelper
|
|||
content.name
|
||||
end
|
||||
end
|
||||
|
||||
# Public: Determines if a given filename is compatible with GitHub::Markup.
|
||||
#
|
||||
# filename - Filename string to check
|
||||
#
|
||||
# Returns boolean
|
||||
def markup?(filename)
|
||||
filename.end_with?(*%w(.mdown .md .markdown .textile .rdoc .org .creole
|
||||
.mediawiki .rst .asciidoc .pod))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,8 @@ class Event < ActiveRecord::Base
|
|||
Pushed = 5
|
||||
Commented = 6
|
||||
Merged = 7
|
||||
Joined = 8 # User joined project
|
||||
Left = 9 # User left project
|
||||
|
||||
belongs_to :project
|
||||
belongs_to :target, polymorphic: true
|
||||
|
@ -37,7 +39,7 @@ class Event < ActiveRecord::Base
|
|||
# - new issue
|
||||
# - merge request
|
||||
def allowed?
|
||||
push? || issue? || merge_request?
|
||||
push? || issue? || merge_request? || membership_changed?
|
||||
end
|
||||
|
||||
def push?
|
||||
|
@ -84,6 +86,18 @@ class Event < ActiveRecord::Base
|
|||
[Closed, Reopened].include?(action)
|
||||
end
|
||||
|
||||
def joined?
|
||||
action == Joined
|
||||
end
|
||||
|
||||
def left?
|
||||
action == Left
|
||||
end
|
||||
|
||||
def membership_changed?
|
||||
joined? || left?
|
||||
end
|
||||
|
||||
def issue
|
||||
target if target_type == "Issue"
|
||||
end
|
||||
|
@ -101,6 +115,10 @@ class Event < ActiveRecord::Base
|
|||
"closed"
|
||||
elsif merged?
|
||||
"merged"
|
||||
elsif joined?
|
||||
'joined'
|
||||
elsif left?
|
||||
'left'
|
||||
else
|
||||
"opened"
|
||||
end
|
||||
|
|
|
@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def automerge!(current_user)
|
||||
if Gitlab::Merge.new(self, current_user).merge
|
||||
if Gitlab::Merge.new(self, current_user).merge && self.unmerged_commits.empty?
|
||||
self.merge!(current_user.id)
|
||||
true
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ class UsersProject < ActiveRecord::Base
|
|||
def self.bulk_delete(project, user_ids)
|
||||
UsersProject.transaction do
|
||||
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
|
||||
users_project.delete
|
||||
users_project.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
class UsersProjectObserver < ActiveRecord::Observer
|
||||
def after_create(users_project)
|
||||
Notify.project_access_granted_email(users_project.id).deliver
|
||||
|
||||
Event.create(
|
||||
project_id: users_project.project.id,
|
||||
action: Event::Joined,
|
||||
author_id: users_project.user.id
|
||||
)
|
||||
end
|
||||
|
||||
def after_update(users_project)
|
||||
Notify.project_access_granted_email(users_project.id).deliver
|
||||
end
|
||||
|
||||
def after_destroy(users_project)
|
||||
Event.create(
|
||||
project_id: users_project.project.id,
|
||||
action: Event::Left,
|
||||
author_id: users_project.user.id
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -90,6 +90,8 @@ module PushEvent
|
|||
|
||||
def push_with_commits?
|
||||
md_ref? && commits.any? && parent_commit && last_commit
|
||||
rescue Grit::NoSuchPathError
|
||||
false
|
||||
end
|
||||
|
||||
def last_push_to_non_root?
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%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"
|
||||
= select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select chosen"
|
||||
= hidden_field_tag :destination, "commits"
|
||||
|
||||
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
|
||||
|
@ -26,8 +26,3 @@
|
|||
%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();
|
||||
});
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -11,3 +11,7 @@
|
|||
.event_feed
|
||||
= render "events/event_push", event: event
|
||||
|
||||
- elsif event.membership_changed?
|
||||
.event_feed
|
||||
= render "events/event_membership_changed", event: event
|
||||
|
||||
|
|
9
app/views/events/_event_membership_changed.html.haml
Normal file
9
app/views/events/_event_membership_changed.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
= image_tag gravatar_icon(event.author_email), class: "avatar"
|
||||
%strong #{event.author_name}
|
||||
%span.event_label{class: event.action_name}= event.action_name
|
||||
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
|
||||
|
|
|
@ -1,8 +1 @@
|
|||
= render "form"
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
$('select#issue_assignee_id').chosen();
|
||||
$('select#issue_milestone_id').chosen();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1 @@
|
|||
= render "form"
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
$('select#issue_assignee_id').chosen();
|
||||
$('select#issue_milestone_id').chosen();
|
||||
});
|
||||
|
||||
|
|
|
@ -34,12 +34,4 @@
|
|||
source: #{raw search_autocomplete_source},
|
||||
select: function(event, ui) { location.href = ui.item.url }
|
||||
});
|
||||
|
||||
$(document).keypress(function(e) {
|
||||
if($(e.target).is(":input")) return;
|
||||
switch(e.which) {
|
||||
case 115: focusSearch();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(){
|
||||
disableButtonIfEmtpyField("#merge_request_title", ".save-btn");
|
||||
$('select#merge_request_assignee_id').chosen();
|
||||
$('select#merge_request_source_branch').chosen();
|
||||
$('select#merge_request_target_branch').chosen();
|
||||
disableButtonIfEmptyField("#merge_request_title", ".save-btn");
|
||||
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() });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
:javascript
|
||||
$(function() {
|
||||
disableButtonIfEmtpyField("#milestone_title", ".save-btn");
|
||||
disableButtonIfEmptyField("#milestone_title", ".save-btn");
|
||||
$( ".datepicker" ).datepicker({
|
||||
dateFormat: "yy-mm-dd",
|
||||
onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
|
||||
|
|
|
@ -1,7 +1 @@
|
|||
= render "form"
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
$('select#issue_assignee_id').chosen();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
= 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, onchange: "this.form.submit();", class: "project-refs-select chosen"
|
||||
= hidden_field_tag :destination, destination
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
$('.project-refs-select').chosen();
|
||||
})
|
||||
|
|
|
@ -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"})
|
||||
|
||||
= 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();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
%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"
|
||||
= select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select chosen"
|
||||
= hidden_field_tag :destination, "tree"
|
||||
= hidden_field_tag :path, params[:path]
|
||||
%li{class: "#{'active' if (controller.controller_name == "refs") }"}
|
||||
|
|
|
@ -43,18 +43,11 @@
|
|||
%i.icon-file
|
||||
= content.name
|
||||
.file_content.wiki
|
||||
- if content.name =~ /\.(md|markdown)$/i
|
||||
= preserve do
|
||||
= markdown(content.data)
|
||||
- else
|
||||
= simple_format(content.data)
|
||||
= raw GitHub::Markup.render(content.name, content.data)
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
$('.project-refs-select').chosen();
|
||||
|
||||
history.pushState({ path: this.path }, '', "#{@history_path}");
|
||||
|
||||
});
|
||||
|
||||
// Load last commit log for each file in tree
|
||||
|
|
|
@ -9,10 +9,9 @@
|
|||
= link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small"
|
||||
= link_to "blame", blame_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small"
|
||||
- if file.text?
|
||||
- if name =~ /\.(md|markdown)$/i
|
||||
- if markup?(name)
|
||||
.file_content.wiki
|
||||
= preserve do
|
||||
= markdown(file.data)
|
||||
= raw GitHub::Markup.render(name, file.data)
|
||||
- else
|
||||
.file_content.code
|
||||
- unless file.empty?
|
||||
|
|
|
@ -38,8 +38,3 @@
|
|||
= preserve do
|
||||
%pre
|
||||
= Gitlab::Encode.utf8 lines.join("\n")
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
$('.project-refs-select').chosen();
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
- 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"
|
||||
|
|
|
@ -33,11 +33,12 @@ app:
|
|||
git_host:
|
||||
admin_uri: git@localhost:gitolite-admin
|
||||
base_path: /home/git/repositories/
|
||||
# hooks_path: /var/lib/gitolite/.gitolite/hooks/ # only needed when gitolite is not installed according the manual
|
||||
# host: localhost
|
||||
hooks_path: /home/git/.gitolite/hooks/
|
||||
gitolite_admin_key: gitlab
|
||||
git_user: git
|
||||
upload_pack: true
|
||||
receive_pack: true
|
||||
# host: localhost
|
||||
# port: 22
|
||||
|
||||
# Git settings
|
||||
|
|
|
@ -102,6 +102,10 @@ class Settings < Settingslogic
|
|||
git_host['admin_uri'] || 'git@localhost:gitolite-admin'
|
||||
end
|
||||
|
||||
def gitolite_admin_key
|
||||
git_host['gitolite_admin_key'] || 'gitlab'
|
||||
end
|
||||
|
||||
def default_projects_limit
|
||||
app['default_projects_limit'] || 10
|
||||
end
|
||||
|
|
|
@ -113,17 +113,20 @@ Generate key:
|
|||
Clone GitLab's fork of the Gitolite source code:
|
||||
|
||||
cd /home/git
|
||||
sudo -H -u git git clone https://github.com/gitlabhq/gitolite.git /home/git/gitolite
|
||||
sudo -H -u git git clone -b gl-v304 https://github.com/gitlabhq/gitolite.git /home/git/gitolite
|
||||
|
||||
Setup:
|
||||
|
||||
cd /home/git
|
||||
sudo -u git -H mkdir bin
|
||||
sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile'
|
||||
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install"
|
||||
sudo -u git sh -c 'gitolite/install -ln /home/git/bin'
|
||||
|
||||
sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub
|
||||
sudo chmod 0444 /home/git/gitlab.pub
|
||||
|
||||
sudo -u git -H sed -i 's/0077/0007/g' /home/git/share/gitolite/conf/example.gitolite.rc
|
||||
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gl-setup -q /home/git/gitlab.pub"
|
||||
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gitolite setup -pk /home/git/gitlab.pub"
|
||||
sudo -u git -H sed -i 's/0077/0007/g' /home/git/.gitolite.rc
|
||||
|
||||
Permissions:
|
||||
|
||||
|
@ -189,8 +192,8 @@ and ensure you have followed all of the above steps carefully.
|
|||
|
||||
#### Setup GitLab hooks
|
||||
|
||||
sudo cp ./lib/hooks/post-receive /home/git/share/gitolite/hooks/common/post-receive
|
||||
sudo chown git:git /home/git/share/gitolite/hooks/common/post-receive
|
||||
sudo cp ./lib/hooks/post-receive /home/git/.gitolite/hooks/common/post-receive
|
||||
sudo chown git:git /home/git/.gitolite/hooks/common/post-receive
|
||||
|
||||
#### Check application status
|
||||
|
||||
|
|
|
@ -15,4 +15,14 @@ Feature: Dashboard
|
|||
And I click "Create Merge Request" link
|
||||
Then I see prefilled new Merge Request page
|
||||
|
||||
Scenario: I should see User joined Project event
|
||||
Given user with name "John Doe" joined project "Shop"
|
||||
When I visit dashboard page
|
||||
Then I should see "John Doe joined project Shop" event
|
||||
|
||||
Scenario: I should see User left Project event
|
||||
Given user with name "John Doe" joined project "Shop"
|
||||
And user with name "John Doe" left project "Shop"
|
||||
When I visit dashboard page
|
||||
Then I should see "John Doe left project Shop" event
|
||||
|
||||
|
|
|
@ -109,3 +109,28 @@ Given /^I have authored merge requests$/ do
|
|||
: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
|
||||
|
||||
|
|
|
@ -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,7 +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)
|
||||
forbidden!
|
||||
end
|
||||
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
|
||||
@abilities ||= begin
|
||||
abilities = Six.new
|
||||
abilities << Ability
|
||||
abilities
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,7 +60,7 @@ module Gitlab
|
|||
if @issue.save
|
||||
present @issue, with: Entities::Issue
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -79,6 +79,8 @@ module Gitlab
|
|||
# PUT /projects/:id/issues/:issue_id
|
||||
put ":id/issues/:issue_id" do
|
||||
@issue = user_project.issues.find(params[:issue_id])
|
||||
authorize! :modify_issue, @issue
|
||||
|
||||
parameters = {
|
||||
title: (params[:title] || @issue.title),
|
||||
description: (params[:description] || @issue.description),
|
||||
|
@ -91,7 +93,7 @@ module Gitlab
|
|||
if @issue.update_attributes(parameters)
|
||||
present @issue, with: Entities::Issue
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,7 +105,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
|
||||
|
|
|
@ -45,7 +45,7 @@ module Gitlab
|
|||
if @milestone.save
|
||||
present @milestone, with: Entities::Milestone
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -61,6 +61,8 @@ module Gitlab
|
|||
# Example Request:
|
||||
# PUT /projects/:id/milestones/:milestone_id
|
||||
put ":id/milestones/:milestone_id" do
|
||||
authorize! :admin_milestone, user_project
|
||||
|
||||
@milestone = user_project.milestones.find(params[:milestone_id])
|
||||
parameters = {
|
||||
title: (params[:title] || @milestone.title),
|
||||
|
@ -72,7 +74,7 @@ module Gitlab
|
|||
if @milestone.update_attributes(parameters)
|
||||
present @milestone, with: Entities::Milestone
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,7 +50,7 @@ module Gitlab
|
|||
if @project.saved?
|
||||
present @project, with: Entities::Project
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -74,6 +74,7 @@ module Gitlab
|
|||
# Example Request:
|
||||
# POST /projects/:id/users
|
||||
post ":id/users" do
|
||||
authorize! :admin_project, user_project
|
||||
user_project.add_users_ids_to_team(params[:user_ids].values, params[:project_access])
|
||||
nil
|
||||
end
|
||||
|
@ -87,6 +88,7 @@ module Gitlab
|
|||
# Example Request:
|
||||
# PUT /projects/:id/add_users
|
||||
put ":id/users" do
|
||||
authorize! :admin_project, user_project
|
||||
user_project.update_users_ids_to_role(params[:user_ids].values, params[:project_access])
|
||||
nil
|
||||
end
|
||||
|
@ -99,6 +101,7 @@ module Gitlab
|
|||
# Example Request:
|
||||
# DELETE /projects/:id/users
|
||||
delete ":id/users" do
|
||||
authorize! :admin_project, user_project
|
||||
user_project.delete_users_ids_from_team(params[:user_ids].values)
|
||||
nil
|
||||
end
|
||||
|
@ -209,7 +212,7 @@ module Gitlab
|
|||
if @snippet.save
|
||||
present @snippet, with: Entities::ProjectSnippet
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -226,6 +229,8 @@ module Gitlab
|
|||
# PUT /projects/:id/snippets/:snippet_id
|
||||
put ":id/snippets/:snippet_id" do
|
||||
@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),
|
||||
|
@ -236,7 +241,7 @@ module Gitlab
|
|||
if @snippet.update_attributes(parameters)
|
||||
present @snippet, with: Entities::ProjectSnippet
|
||||
else
|
||||
error!({'message' => '404 Not found'}, 404)
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -249,6 +254,8 @@ module Gitlab
|
|||
# DELETE /projects/:id/snippets/:snippet_id
|
||||
delete ":id/snippets/:snippet_id" do
|
||||
@snippet = user_project.snippets.find(params[:snippet_id])
|
||||
authorize! :modify_snippet, @snippet
|
||||
|
||||
@snippet.destroy
|
||||
end
|
||||
|
||||
|
@ -277,10 +284,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)
|
||||
|
|
|
@ -35,7 +35,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def enable_automerge
|
||||
config.admin_all_repo!(project)
|
||||
config.admin_all_repo!
|
||||
end
|
||||
|
||||
alias_method :create_repository, :update_repository
|
||||
|
|
|
@ -148,18 +148,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 = ""
|
||||
|
||||
# Read gitolite-admin user
|
||||
#
|
||||
begin
|
||||
repo = conf.get_repo("gitolite-admin")
|
||||
owner_name = repo.permissions[0]["RW+"][""][0]
|
||||
raise StandardError if owner_name.blank?
|
||||
rescue => ex
|
||||
puts "Can't determine gitolite-admin owner".red
|
||||
raise StandardError
|
||||
end
|
||||
owner_name = Gitlab.config.gitolite_admin_key
|
||||
|
||||
# @ALL repos premission for gitolite owner
|
||||
repo_name = "@all"
|
||||
|
|
|
@ -21,8 +21,7 @@ module Gitlab
|
|||
if output =~ /CONFLICT/
|
||||
false
|
||||
else
|
||||
repo.git.push({}, "origin", merge_request.target_branch)
|
||||
true
|
||||
!!repo.git.push({}, "origin", merge_request.target_branch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
15
spec/helpers/tree_helper_spec.rb
Normal file
15
spec/helpers/tree_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe TreeHelper do
|
||||
describe '#markup?' do
|
||||
%w(mdown md markdown textile rdoc org creole mediawiki rst asciidoc pod).each do |type|
|
||||
it "returns true for #{type} files" do
|
||||
markup?("README.#{type}").should be_true
|
||||
end
|
||||
end
|
||||
|
||||
it "returns false when given a non-markup filename" do
|
||||
markup?('README.rb').should_not be_true
|
||||
end
|
||||
end
|
||||
end
|
|
@ -49,4 +49,26 @@ describe Event do
|
|||
it { @event.branch_name.should == "master" }
|
||||
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)
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,14 @@ describe UsersProjectObserver do
|
|||
Notify.should_receive(:project_access_granted_email).with(users_project.id).and_return(double(deliver: true))
|
||||
subject.after_create(users_project)
|
||||
end
|
||||
it "should create new event" do
|
||||
Event.should_receive(:create).with(
|
||||
project_id: users_project.project.id,
|
||||
action: Event::Joined,
|
||||
author_id: users_project.user.id
|
||||
)
|
||||
subject.after_create(users_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#after_update" do
|
||||
|
@ -37,4 +45,23 @@ describe UsersProjectObserver do
|
|||
subject.after_update(users_project)
|
||||
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]
|
||||
)
|
||||
end
|
||||
end
|
||||
it "should create new event" do
|
||||
Event.should_receive(:create).with(
|
||||
project_id: users_project.project.id,
|
||||
action: Event::Left,
|
||||
author_id: users_project.user.id
|
||||
)
|
||||
subject.after_destroy(users_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,7 +86,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
|
||||
|
||||
|
|
Loading…
Reference in a new issue