Commit e6ce4729 authored by Alex Denisov's avatar Alex Denisov

master merged

parents 77bfc591 61049424
## Contribute to GitLab
If you want to contribute to GitLab, follow this process:
1. Fork the project
2. Create a feature branch
3. Code
4. Create a pull request
We only accept pull requests if:
* Your code has proper tests and all tests pass
* Your code can be merged w/o problems
* It wont broke existing functionality
* Its a quality code
* We like it :)
## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
## Running tests
To run the specs for GitLab, you need to run seeds for test db.
cd gitlabhq
rake db:seed_fu RAILS_ENV=test
Then you can run the test suite with rake:
rake gitlab:test
source "http://rubygems.org" source "http://rubygems.org"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
end
def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "3.2.8" gem "rails", "3.2.8"
# Supported DBs # Supported DBs
...@@ -8,6 +16,10 @@ gem "mysql2" ...@@ -8,6 +16,10 @@ gem "mysql2"
# Auth # Auth
gem "devise", "~> 2.1.0" gem "devise", "~> 2.1.0"
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
# GITLAB patched libs # GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
...@@ -98,21 +110,28 @@ group :development do ...@@ -98,21 +110,28 @@ group :development do
end end
group :development, :test do group :development, :test do
gem 'spinach-rails'
gem "rspec-rails" gem "rspec-rails"
gem "capybara" gem "capybara"
gem "capybara-webkit" gem "capybara-webkit"
gem "headless" gem "headless"
gem "autotest"
gem "autotest-rails"
gem "pry" gem "pry"
gem "awesome_print" gem "awesome_print"
gem "database_cleaner" gem "database_cleaner"
gem "launchy" gem "launchy"
gem 'factory_girl_rails' gem 'factory_girl_rails'
# Guard
gem 'guard-rspec'
gem 'guard-spinach'
# Notification
gem 'rb-fsevent', :require => darwin_only('rb-fsevent')
gem 'growl', :require => darwin_only('growl')
gem 'rb-inotify', :require => linux_only('rb-inotify')
end end
group :test do group :test do
gem 'cucumber-rails', :require => false
gem "simplecov", :require => false gem "simplecov", :require => false
gem "shoulda-matchers" gem "shoulda-matchers"
gem 'email_spec' gem 'email_spec'
......
...@@ -68,7 +68,6 @@ GIT ...@@ -68,7 +68,6 @@ GIT
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
ZenTest (4.8.1)
actionmailer (3.2.8) actionmailer (3.2.8)
actionpack (= 3.2.8) actionpack (= 3.2.8)
mail (~> 2.4.4) mail (~> 2.4.4)
...@@ -100,10 +99,6 @@ GEM ...@@ -100,10 +99,6 @@ GEM
rails (~> 3.0) rails (~> 3.0)
addressable (2.2.8) addressable (2.2.8)
arel (3.0.2) arel (3.0.2)
autotest (4.4.6)
ZenTest (>= 4.4.1)
autotest-rails (4.1.2)
ZenTest (~> 4.5)
awesome_print (1.0.2) awesome_print (1.0.2)
bcrypt-ruby (3.0.1) bcrypt-ruby (3.0.1)
blankslate (2.1.2.4) blankslate (2.1.2.4)
...@@ -137,16 +132,8 @@ GEM ...@@ -137,16 +132,8 @@ GEM
execjs execjs
coffee-script-source (1.3.3) coffee-script-source (1.3.3)
colored (1.2) colored (1.2)
colorize (0.5.8)
crack (0.3.1) crack (0.3.1)
cucumber (1.2.1)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.11.0)
json (>= 1.4.6)
cucumber-rails (1.3.0)
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
nokogiri (>= 1.5.0)
daemons (1.1.8) daemons (1.1.8)
database_cleaner (0.8.0) database_cleaner (0.8.0)
devise (2.1.2) devise (2.1.2)
...@@ -171,12 +158,13 @@ GEM ...@@ -171,12 +158,13 @@ GEM
factory_girl_rails (4.0.0) factory_girl_rails (4.0.0)
factory_girl (~> 4.0.0) factory_girl (~> 4.0.0)
railties (>= 3.0.0) railties (>= 3.0.0)
faraday (0.8.4)
multipart-post (~> 1.1)
ffaker (1.14.0) ffaker (1.14.0)
ffi (1.0.11) ffi (1.0.11)
foreman (0.47.0) foreman (0.47.0)
thor (>= 0.13.6) thor (>= 0.13.6)
gherkin (2.11.0) gherkin-ruby (0.2.1)
json (>= 1.4.6)
git (1.2.5) git (1.2.5)
github-markup (0.7.4) github-markup (0.7.4)
gitlab_meta (2.9) gitlab_meta (2.9)
...@@ -186,6 +174,15 @@ GEM ...@@ -186,6 +174,15 @@ GEM
multi_xml multi_xml
rack rack
rack-mount rack-mount
growl (1.0.3)
guard (1.3.2)
listen (>= 0.4.2)
thor (>= 0.14.6)
guard-rspec (1.2.1)
guard (>= 1.1)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
haml (3.1.6) haml (3.1.6)
haml-rails (0.3.4) haml-rails (0.3.4)
actionpack (~> 3.0) actionpack (~> 3.0)
...@@ -199,6 +196,7 @@ GEM ...@@ -199,6 +196,7 @@ GEM
httparty (0.8.3) httparty (0.8.3)
multi_json (~> 1.0) multi_json (~> 1.0)
multi_xml multi_xml
httpauth (0.1)
i18n (0.6.1) i18n (0.6.1)
journey (1.0.4) journey (1.0.4)
jquery-rails (2.0.2) jquery-rails (2.0.2)
...@@ -208,6 +206,8 @@ GEM ...@@ -208,6 +206,8 @@ GEM
jquery-rails jquery-rails
railties (>= 3.1.0) railties (>= 3.1.0)
json (1.7.5) json (1.7.5)
jwt (0.1.5)
multi_json (>= 1.0)
kaminari (0.14.0) kaminari (0.14.0)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
...@@ -219,6 +219,7 @@ GEM ...@@ -219,6 +219,7 @@ GEM
libv8 (3.3.10.4) libv8 (3.3.10.4)
libwebsocket (0.1.3) libwebsocket (0.1.3)
addressable addressable
listen (0.5.0)
mail (2.4.4) mail (2.4.4)
i18n (>= 0.4.0) i18n (>= 0.4.0)
mime-types (~> 1.16) mime-types (~> 1.16)
...@@ -229,12 +230,35 @@ GEM ...@@ -229,12 +230,35 @@ GEM
sprockets (~> 2.0) sprockets (~> 2.0)
multi_json (1.3.6) multi_json (1.3.6)
multi_xml (0.5.1) multi_xml (0.5.1)
multipart-post (1.1.5)
mysql2 (0.3.11) mysql2 (0.3.11)
net-ldap (0.2.2) net-ldap (0.2.2)
nokogiri (1.5.3) nokogiri (1.5.3)
oauth (0.4.7)
oauth2 (0.8.0)
faraday (~> 0.8)
httpauth (~> 0.1)
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
omniauth (1.1.0) omniauth (1.1.0)
hashie (~> 1.2) hashie (~> 1.2)
rack rack
omniauth-github (1.0.3)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-google-oauth2 (0.1.13)
omniauth (~> 1.0)
omniauth-oauth2
omniauth-oauth (1.0.1)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.1.0)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
omniauth-twitter (0.0.13)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
orm_adapter (0.3.0) orm_adapter (0.3.0)
polyglot (0.3.3) polyglot (0.3.3)
posix-spawn (0.3.6) posix-spawn (0.3.6)
...@@ -274,6 +298,9 @@ GEM ...@@ -274,6 +298,9 @@ GEM
raindrops (0.9.0) raindrops (0.9.0)
rake (0.9.2.2) rake (0.9.2.2)
raphael-rails (1.5.2) raphael-rails (1.5.2)
rb-fsevent (0.9.1)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
rdoc (3.12) rdoc (3.12)
json (~> 1.4) json (~> 1.4)
redcarpet (2.1.1) redcarpet (2.1.1)
...@@ -336,6 +363,13 @@ GEM ...@@ -336,6 +363,13 @@ GEM
tilt (~> 1.3, >= 1.3.3) tilt (~> 1.3, >= 1.3.3)
six (0.2.0) six (0.2.0)
slop (2.4.4) slop (2.4.4)
spinach (0.5.2)
colorize
gherkin-ruby (~> 0.2.0)
spinach-rails (0.1.8)
capybara (~> 1)
railties (>= 3)
spinach (>= 0.4)
sprockets (2.1.3) sprockets (2.1.3)
hike (~> 1.2) hike (~> 1.2)
rack (~> 1.0) rack (~> 1.0)
...@@ -378,8 +412,6 @@ PLATFORMS ...@@ -378,8 +412,6 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
acts-as-taggable-on (= 2.3.1) acts-as-taggable-on (= 2.3.1)
annotate! annotate!
autotest
autotest-rails
awesome_print awesome_print
bootstrap-sass (= 2.0.4) bootstrap-sass (= 2.0.4)
capybara capybara
...@@ -389,7 +421,6 @@ DEPENDENCIES ...@@ -389,7 +421,6 @@ DEPENDENCIES
chosen-rails chosen-rails
coffee-rails (= 3.2.2) coffee-rails (= 3.2.2)
colored colored
cucumber-rails
database_cleaner database_cleaner
devise (~> 2.1.0) devise (~> 2.1.0)
draper draper
...@@ -404,6 +435,9 @@ DEPENDENCIES ...@@ -404,6 +435,9 @@ DEPENDENCIES
grack! grack!
grape (~> 0.2.1) grape (~> 0.2.1)
grit! grit!
growl
guard-rspec
guard-spinach
haml-rails haml-rails
headless headless
httparty httparty
...@@ -415,12 +449,18 @@ DEPENDENCIES ...@@ -415,12 +449,18 @@ DEPENDENCIES
linguist (~> 1.0.0)! linguist (~> 1.0.0)!
modernizr (= 2.5.3) modernizr (= 2.5.3)
mysql2 mysql2
omniauth
omniauth-github
omniauth-google-oauth2
omniauth-ldap! omniauth-ldap!
omniauth-twitter
pry pry
pygments.rb! pygments.rb!
rack-mini-profiler rack-mini-profiler
rails (= 3.2.8) rails (= 3.2.8)
raphael-rails (= 1.5.2) raphael-rails (= 1.5.2)
rb-fsevent
rb-inotify
redcarpet (~> 2.1.1) redcarpet (~> 2.1.1)
resque (~> 1.20.0) resque (~> 1.20.0)
resque_mailer resque_mailer
...@@ -432,6 +472,7 @@ DEPENDENCIES ...@@ -432,6 +472,7 @@ DEPENDENCIES
shoulda-matchers shoulda-matchers
simplecov simplecov
six six
spinach-rails
sqlite3 sqlite3
stamp stamp
test_after_commit test_after_commit
......
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => false do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara request specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
end
guard 'spinach' do
watch(%r|^features/(.*)\.feature|)
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
"features/#{m[1]}#{m[2]}.feature"
end
end
...@@ -6,3 +6,7 @@ $ -> ...@@ -6,3 +6,7 @@ $ ->
elems.val('').attr 'disabled', true elems.val('').attr 'disabled', true
else else
elems.removeAttr 'disabled' elems.removeAttr 'disabled'
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
//= require jquery.endless-scroll //= require jquery.endless-scroll
//= require jquery.highlight //= require jquery.highlight
//= require jquery.waitforimages //= require jquery.waitforimages
//= require bootstrap-modal //= require bootstrap
//= require modernizr //= require modernizr
//= require chosen-jquery //= require chosen-jquery
//= require raphael //= require raphael
......
...@@ -24,6 +24,9 @@ $ -> ...@@ -24,6 +24,9 @@ $ ->
# Click a .one_click_select field, select the contents # Click a .one_click_select field, select the contents
$(".one_click_select").live 'click', -> $(this).select() $(".one_click_select").live 'click', -> $(this).select()
# Initialize chosen selects
$('select.chosen').chosen()
# Disable form buttons while a form is submitting # Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', this) buttons = $('[type="submit"]', this)
......
var NoteList = {
notes_path: null,
target_params: null,
target_id: 0,
target_type: null,
first_id: 0,
last_id: 0,
disable:false,
init:
function(tid, tt, path) {
this.notes_path = path + ".js";
this.target_id = tid;
this.target_type = tt;
this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
// get notes
this.getContent();
// get new notes every n seconds
this.initRefresh();
$('.delete-note').live('ajax:success', function() {
$(this).closest('li').fadeOut(); });
$(".note-form-holder").live("ajax:before", function(){
$(".submit_note").disable()
})
$(".note-form-holder").live("ajax:complete", function(){
$(".submit_note").enable()
})
disableButtonIfEmptyField(".note-text", ".submit_note");
$(".note-text").live("focus", function(){
$(this).css("height", "80px");
$('.note_advanced_opts').show();
});
$("#note_attachment").change(function(e){
var val = $('.input-file').val();
var filename = val.replace(/^.*[\\\/]/, '');
$(".file_name").text(filename);
});
},
/**
* Load new notes to fresh list called 'new_notes_list':
* - Replace 'new_notes_list' with new list every n seconds
* - Append new notes to this list after submit
*/
initRefresh:
function() {
// init timer
var intNew = setInterval("NoteList.getNew()", 10000);
},
replace:
function(html) {
$("#new_notes_list").html(html);
},
prepend:
function(id, html) {
if(id != this.last_id) {
$("#new_notes_list").prepend(html);
}
},
getNew:
function() {
// refersh notes list
$.ajax({
type: "GET",
url: this.notes_path,
data: "last_id=" + this.last_id + this.target_params,
dataType: "script"});
},
refresh:
function() {
// refersh notes list
$.ajax({
type: "GET",
url: this.notes_path,
data: "first_id=" + this.first_id + "&last_id=" + this.last_id + this.target_params,
dataType: "script"});
},
/**
* Init load of notes:
* 1. Get content with ajax call
* 2. Set content of notes list with loaded one
*/
getContent:
function() {
$.ajax({
type: "GET",
url: this.notes_path,
data: "?" + this.target_params,
complete: function(){ $('.status').removeClass("loading")},
beforeSend: function() { $('.status').addClass("loading") },
dataType: "script"});
},
setContent:
function(fid, lid, html) {
this.last_id = lid;
this.first_id = fid;
$("#notes-list").html(html);
// Init infinite scrolling
this.initLoadMore();
},
/**
* Paging for old notes when scroll to bottom:
* 1. Init scroll events with 'initLoadMore'
* 2. Load onlder notes with 'getOld' method
* 3. append old notes to bottom of list with 'append'
*
*/
getOld:
function() {
$('.loading').show();
$.ajax({
type: "GET",
url: this.notes_path,
data: "first_id=" + this.first_id + this.target_params,
complete: function(){ $('.status').removeClass("loading")},
beforeSend: function() { $('.status').addClass("loading") },
dataType: "script"});
},
append:
function(id, html) {
if(this.first_id == id) {
this.disable = true;
} else {
this.first_id = id;
$("#notes-list").append(html);
}
},
initLoadMore:
function() {
$(document).endlessScroll({
bottomPixels: 400,
fireDelay: 1000,
fireOnce:true,
ceaseFire: function() {
return NoteList.disable;
},
callback: function(i) {
NoteList.getOld();
}
});
}
};
var PerLineNotes = {
init:
function() {
$(".line_note_link, .line_note_reply_link").live("click", function(e) {
var form = $(".per_line_form");
$(this).closest("tr").after(form);
form.find("#note_line_code").val($(this).attr("line_code"));
form.show();
return false;
});
disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
}
}
var NoteList = {
notes_path: null,
target_params: null,
target_id: 0,
target_type: null,
top_id: 0,
bottom_id: 0,
loading_more_disabled: false,
reversed: false,
init:
function(tid, tt, path) {
this.notes_path = path + ".js";
this.target_id = tid;
this.target_type = tt;
this.reversed = $("#notes-list").hasClass("reversed");
this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
// get initial set of notes
this.getContent();
$("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() {
$(this).closest('li').fadeOut(function() {
$(this).remove();
NoteList.updateVotes();
});
});
$(".note-form-holder").on("ajax:before", function(){
$(".submit_note").disable();
})
$(".note-form-holder").on("ajax:complete", function(){
$(".submit_note").enable();
})
disableButtonIfEmptyField(".note-text", ".submit_note");
$("#note_attachment").change(function(e){
var val = $('.input-file').val();
var filename = val.replace(/^.*[\\\/]/, '');
$(".file_name").text(filename);
});
if(this.reversed) {
var textarea = $(".note-text");
$('.note_advanced_opts').hide();
textarea.css("height", "40px");
textarea.on("focus", function(){
$(this).css("height", "80px");
$('.note_advanced_opts').show();
});
}
},
/**
* Handle loading the initial set of notes.
* And set up loading more notes when scrolling to the bottom of the page.
*/
/**
* Gets an inital set of notes.
*/
getContent:
function() {
$.ajax({
type: "GET",
url: this.notes_path,
data: "?" + this.target_params,
complete: function(){ $('.notes-status').removeClass("loading")},
beforeSend: function() { $('.notes-status').addClass("loading") },
dataType: "script"});
},
/**
* Called in response to getContent().
* Replaces the content of #notes-list with the given html.
*/
setContent:
function(first_id, last_id, html) {
this.top_id = first_id;
this.bottom_id = last_id;
$("#notes-list").html(html);
// init infinite scrolling
this.initLoadMore();
// init getting new notes
if (this.reversed) {
this.initRefreshNew();
}
},
/**
* Handle loading more notes when scrolling to the bottom of the page.
* The id of the last note in the list is in this.bottom_id.
*
* Set up refreshing only new notes after all notes have been loaded.
*/
/**
* Initializes loading more notes when scrolling to the bottom of the page.
*/
initLoadMore:
function() {
$(document).endlessScroll({
bottomPixels: 400,
fireDelay: 1000,
fireOnce:true,
ceaseFire: function() {
return NoteList.loading_more_disabled;
},
callback: function(i) {
NoteList.getMore();
}
});
},
/**
* Gets an additional set of notes.
*/
getMore:
function() {
// only load more notes if there are no "new" notes
$('.loading').show();
$.ajax({
type: "GET",
url: this.notes_path,
data: "loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id + this.target_params,
complete: function(){ $('.notes-status').removeClass("loading")},
beforeSend: function() { $('.notes-status').addClass("loading") },
dataType: "script"});
},
/**
* Called in response to getMore().
* Append notes to #notes-list.
*/
appendMoreNotes:
function(id, html) {
if(id != this.bottom_id) {
this.bottom_id = id;
$("#notes-list").append(html);
}
},
/**
* Called in response to getMore().
* Disables loading more notes when scrolling to the bottom of the page.
* Initalizes refreshing new notes.
*/
finishedLoadingMore:
function() {
this.loading_more_disabled = true;
// from now on only get new notes
if (!this.reversed) {
this.initRefreshNew();
}
// make sure we are up to date
this.updateVotes();
},
/**
* Handle refreshing and adding of new notes.
*
* New notes are all notes that are created after the site has been loaded.
* The "old" notes are in #notes-list the "new" ones will be in #new-notes-list.
* The id of the last "old" note is in this.bottom_id.
*/
/**
* Initializes getting new notes every n seconds.
*/
initRefreshNew:
function() {
setInterval("NoteList.getNew()", 10000);
},
/**
* Gets the new set of notes.
*/
getNew:
function() {
$.ajax({
type: "GET",
url: this.notes_path,
data: "loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id) + this.target_params,
dataType: "script"});
},
/**
* Called in response to getNew().
* Replaces the content of #new-notes-list with the given html.
*/
replaceNewNotes:
function(html) {
$("#new-notes-list").html(html);
this.updateVotes();
},
/**
* Adds a single note to #new-notes-list.
*/
appendNewNote:
function(id, html) {
if (this.reversed) {
$("#new-notes-list").prepend(html);
} else {
$("#new-notes-list").append(html);
}
this.updateVotes();
},
/**
* Recalculates the votes and updates them (if they are displayed at all).
*
* Assumes all relevant notes are displayed (i.e. there are no more notes to
* load via getMore()).
* Might produce inaccurate results when not all notes have been loaded and a
* recalculation is triggered (e.g. when deleting a note).
*/
updateVotes:
function() {
var votes = $("#votes .votes");
var notes = $("#notes-list, #new-notes-list").find(".note.vote");
// only update if there is a vote display
if (votes.size()) {
var upvotes = notes.filter(".upvote").size();
var downvotes = notes.filter(".downvote").size();
var votesCount = upvotes + downvotes;
var upvotesPercent = votesCount ? (100.0 / votesCount * upvotes) : 0;
var downvotesPercent = votesCount ? (100.0 - upvotesPercent) : 0;
// change vote bar lengths
votes.find(".bar-success").css("width", upvotesPercent+"%");
votes.find(".bar-danger").css("width", downvotesPercent+"%");
// replace vote numbers
votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes));
votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes));
}
}
};
var PerLineNotes = {
init:
function() {
/**
* Called when clicking on the "add note" or "reply" button for a diff line.
*
* Shows the note form below the line.
* Sets some hidden fields in the form.
*/
$(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) {
var form = $(".per_line_form");
$(this).closest("tr").after(form);
form.find("#note_line_code").val($(this).data("lineCode"));
form.show();
return false;
});
disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
/**
* Called in response to successfully deleting a note on a diff line.
*
* Removes the actual note from view.
* Removes the reply button if the last note for that line has been removed.
*/
$(".diff_file_content").on("ajax:success", ".delete-note", function() {
var trNote = $(this).closest("tr");
trNote.fadeOut(function() {
$(this).remove();
});
// check if this is the last note for this line
// elements must really be removed for this to work reliably
var trLine = trNote.prev();
var trRpl = trNote.next();
if (trLine.hasClass("line_holder") && trRpl.hasClass("reply")) {
trRpl.fadeOut(function() { $(this).remove(); });
}
});
}
}
...@@ -10,11 +10,15 @@ window.Projects = -> ...@@ -10,11 +10,15 @@ window.Projects = ->
$('form #project_default_branch').chosen() $('form #project_default_branch').chosen()
disableButtonIfEmptyField '#project_name', '.project-submit' disableButtonIfEmptyField '#project_name', '.project-submit'
# Git clone panel switcher
$ -> $ ->
# Git clone panel switcher
scope = $ '.project_clone_holder' scope = $ '.project_clone_holder'
if scope.length > 0 if scope.length > 0
$('a, button', scope).click -> $('a, button', scope).click ->
$('a, button', scope).removeClass 'active' $('a, button', scope).removeClass 'active'
$(@).addClass 'active' $(@).addClass 'active'
$('#project_clone', scope).val $(@).data 'clone' $('#project_clone', scope).val $(@).data 'clone'
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
...@@ -145,6 +145,19 @@ span.update-author { ...@@ -145,6 +145,19 @@ span.update-author {
.label { .label {
background-color: #474D57; background-color: #474D57;
&.label-tag {
background: none;
border: none;
padding:4px 6px;
color:#444;
text-shadow:0 0 1px #fff;
&.grouped {
float: left;
margin-right: 6px;
padding: 6px;
}
}
&.label-issue { &.label-issue {
background-color: #eee; background-color: #eee;
border: 1px solid #ccc; border: 1px solid #ccc;
...@@ -158,6 +171,18 @@ span.update-author { ...@@ -158,6 +171,18 @@ span.update-author {
padding: 6px; padding: 6px;
} }
} }
&.label-success {
background-color: #8D8;
color: #333;
text-shadow: 0 1px 1px white;
}
&.label-error {
background-color: #D88;
color: #333;
text-shadow: 0 1px 1px white;
}
} }
.event_label { .event_label {
...@@ -181,11 +206,12 @@ span.update-author { ...@@ -181,11 +206,12 @@ span.update-author {
} }
&.joined { &.joined {
background-color: #1cb9ff; background-color: #1ca9dd;
} }
&.left { &.left {
background-color: #ff5057; background-color: #888;
float:none;
} }
} }
...@@ -414,13 +440,48 @@ p.time { ...@@ -414,13 +440,48 @@ p.time {
} }
} }
.upvotes { .votes {
font-size: 14px; font-size: 13px;
font-weight: bold; line-height: 15px;
.progress {
height: 4px;
margin: 0;
.bar {
float: left;
height: 100%;
}
.bar-success {
background-color: #468847;
@include bg-gradient(#62C462, #51A351);
}
.bar-danger {
background-color: #B94A48;
@include bg-gradient(#EE5F5B, #BD362F);
}
}
.upvotes {
display: inline-block;
color: #468847; color: #468847;
text-align: right; }
padding: 4px; .downvotes {
margin: 2px; display: inline-block;
color: #B94A48;
}
}
.votes-block {
margin: 14px 6px 6px 0;
.downvotes {
float: right;
}
}
.votes-inline {
display: inline-block;
margin: 0 8px;
.progress {
display: inline-block;
padding: 0 0 2px;
width: 45px;
}
} }
/* Fix for readme code (stopped it from being yellow) */ /* Fix for readme code (stopped it from being yellow) */
...@@ -624,7 +685,7 @@ li.note { ...@@ -624,7 +685,7 @@ li.note {
margin-right:40px; margin-right:40px;
.prev { .prev {
@extend .borders; @extend .thumbnail;
height:120px; height:120px;
width:175px; width:175px;
margin-bottom:10px; margin-bottom:10px;
...@@ -653,3 +714,31 @@ li.note { ...@@ -653,3 +714,31 @@ li.note {
text-align:center; text-align:center;
margin-bottom:10px; margin-bottom:10px;
} }
.oauth_select_holder {
padding:20px;
img {
padding:5px;
margin-right:10px;
}
.active {
img {
border:1px solid #ccc;
background:$hover;
@include border-radius(5px);
}
}
}
.btn-build-token {
float: left;
padding: 6px 20px;
margin-right: 12px;
}
.gitlab-promo {
a {
color:#aaa;
margin-right: 30px;
}
}
...@@ -65,6 +65,10 @@ ...@@ -65,6 +65,10 @@
border-color: #CCC; border-color: #CCC;
@include solid_shade; @include solid_shade;
&.white {
background:#fff;
}
ul { ul {
margin:0; margin:0;
} }
...@@ -142,4 +146,8 @@ ...@@ -142,4 +146,8 @@
border:none; border:none;
} }
} }
.ui-box-body {
padding:10px;
}
} }
...@@ -33,7 +33,29 @@ ...@@ -33,7 +33,29 @@
.nav-pills a:hover { background-color:#888; } .nav-pills a:hover { background-color:#888; }
.nav-pills .active a { background-color: $style_color; } .nav-pills .active a { background-color: $style_color; }
.nav-tabs > li > a, .nav-pills > li > a { color:$style_color; } .nav-tabs > li > a, .nav-pills > li > a { color:$style_color; }
.nav-tabs > .active > a { font-weight:bold; } .nav.nav-tabs {
li {
> a {
padding:8px 20px;
margin-right: 7px;
border-color: #EEE;
color:#888;
border-bottom: 1px solid #ddd;
.badge {
background-color: #eee;
color:#888;
text-shadow:0 1px 1px #fff;
}
}
&.active {
> a {
border-color: #CCC;
border-bottom: 1px solid #fff;
color:#333;
}
}
}
}
/** ALERT MESSAGES **/ /** ALERT MESSAGES **/
.alert-message { @extend .alert; } .alert-message { @extend .alert; }
...@@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; } ...@@ -50,3 +72,13 @@ img.lil_av { padding-left: 4px; padding-right:3px; }
/** HELPERS **/ /** HELPERS **/
.nothing_here_message { text-align:center; padding:20px; color:#777; } .nothing_here_message { text-align:center; padding:20px; color:#777; }
p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; } p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; }
/** FORMS **/
input[type='search'].search-text-input {
background-image: url("icon-search.png");
background-repeat: no-repeat;
background-position: 10px;
padding-left:25px;
@include border-radius(4px);
border:1px solid #ccc;
}
...@@ -135,7 +135,6 @@ $hover: #fdf5d9; ...@@ -135,7 +135,6 @@ $hover: #fdf5d9;
*/ */
@import "common.scss"; @import "common.scss";
/** /**
* Styles related to specific part of app * Styles related to specific part of app
*/ */
...@@ -161,6 +160,11 @@ $hover: #fdf5d9; ...@@ -161,6 +160,11 @@ $hover: #fdf5d9;
*/ */
@import "sections/notes.scss"; @import "sections/notes.scss";
/**
* This file represent profile styles
*/
@import "sections/profile.scss";
/** /**
* Devise styles * Devise styles
*/ */
......
...@@ -20,15 +20,25 @@ ...@@ -20,15 +20,25 @@
.chzn-drop { .chzn-drop {
margin:7px 0; margin:7px 0;
border: 1px solid #CCC; min-width: 400px;
min-width: 300px; border: 2px solid $blue_link;
@include border-radius(4px);
.chzn-results { .chzn-results {
max-height:300px; max-height:300px;
.group-result {
color: $blue_link;
}
.active-result {
&.highlighted {
background: $blue_link;
}
}
} }
.chzn-search input { .chzn-search input {
min-width:200px; min-width:365px;
} }
} }
......
...@@ -105,6 +105,7 @@ input.check_all_issues { ...@@ -105,6 +105,7 @@ input.check_all_issues {
width:100px; width:100px;
} }
/** /**
* Milestones list * Milestones list
* *
......
...@@ -88,11 +88,10 @@ li.merge_request { ...@@ -88,11 +88,10 @@ li.merge_request {
@include round-borders-all(4px); @include round-borders-all(4px);
padding:2px 4px; padding:2px 4px;
border:none; border:none;
font-size:13px; font-size:14px;
background: #474D57; background: #474D57;
color:#fff; color:#fff;
font-weight:bold; font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
font-family: monospace;
} }
.mr_source_commit, .mr_source_commit,
......
...@@ -55,7 +55,6 @@ ul.main_menu { ...@@ -55,7 +55,6 @@ ul.main_menu {
&.current { &.current {
background-color:#D5D5D5; background-color:#D5D5D5;
border-bottom: 1px solid #AAA;
border-right: 1px solid #BBB; border-right: 1px solid #BBB;
border-left: 1px solid #BBB; border-left: 1px solid #BBB;
border-radius: 0 0 1px 1px; border-radius: 0 0 1px 1px;
......
...@@ -3,17 +3,13 @@ ...@@ -3,17 +3,13 @@
* *
*/ */
#notes-list, #notes-list,
#new_notes_list { #new-notes-list {
display:block; display:block;
list-style:none; list-style:none;
margin:0px; margin:0px;
padding:0px; padding:0px;
} }
#new_notes_list li:last-child{
border-bottom:1px solid #aaa;
}
.issue_notes, .issue_notes,
.wiki_notes { .wiki_notes {
.note_content { .note_content {
...@@ -30,9 +26,6 @@ ...@@ -30,9 +26,6 @@
} }
#new_note { #new_note {
.note-text {
height:40px;
}
.attach_holder { .attach_holder {
display:none; display:none;
} }
...@@ -48,7 +41,6 @@ ...@@ -48,7 +41,6 @@
.note { .note {
padding: 8px 0; padding: 8px 0;
border-bottom: 1px solid #eee;
overflow: hidden; overflow: hidden;
display: block; display: block;
img {float: left; margin-right: 10px;} img {float: left; margin-right: 10px;}
...@@ -70,6 +62,23 @@ ...@@ -70,6 +62,23 @@
.delete-note { display:block; } .delete-note { display:block; }
} }
} }
#notes-list:not(.reversed) .note,
#new-notes-list:not(.reversed) .note {
border-bottom: 1px solid #eee;
}
#notes-list.reversed .note,
#new-notes-list.reversed .note {
border-top: 1px solid #eee;
}
/* mark vote notes */
.voting_notes .note {
padding: 8px 0;
}
.notes-status {
margin: 18px;
}
p.notify_controls input{ p.notify_controls input{
......
.profile_history {
.event_feed {
min-height:20px;
.avatar {
width:20px;
}
}
}
...@@ -3,29 +3,30 @@ module Notes ...@@ -3,29 +3,30 @@ module Notes
def execute def execute
target_type = params[:target_type] target_type = params[:target_type]
target_id = params[:target_id] target_id = params[:target_id]
first_id = params[:first_id] after_id = params[:after_id]
last_id = params[:last_id] before_id = params[:before_id]
@notes = case target_type @notes = case target_type
when "commit" when "commit"
then project.commit_notes(project.commit(target_id)).fresh.limit(20) project.commit_notes(project.commit(target_id)).fresh.limit(20)
when "snippet"
then project.snippets.find(target_id).notes
when "wall"
then project.common_notes.order("created_at DESC").fresh.limit(50)
when "issue" when "issue"
then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20) project.issues.find(target_id).notes.inc_author.fresh.limit(20)
when "merge_request" when "merge_request"
then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20) project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20)
when "snippet"
project.snippets.find(target_id).notes.fresh
when "wall"
# this is the only case, where the order is DESC
project.common_notes.order("created_at DESC, id DESC").limit(50)
when "wiki" when "wiki"
then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20] project.wiki_notes.limit(20)
end end
@notes = if last_id @notes = if after_id
@notes.where("id > ?", last_id) @notes.where("id > ?", after_id)
elsif first_id elsif before_id
@notes.where("id < ?", first_id) @notes.where("id < ?", before_id)
else else
@notes @notes
end end
......
class Admin::DashboardController < ApplicationController class Admin::DashboardController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index def index
@workers = Resque.workers @workers = Resque.workers
@pending_jobs = Resque.size(:post_receive) @pending_jobs = Resque.size(:post_receive)
......
class Admin::HooksController < ApplicationController class Admin::HooksController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index def index
@hooks = SystemHook.all @hooks = SystemHook.all
@hook = SystemHook.new @hook = SystemHook.new
......
class Admin::LogsController < ApplicationController class Admin::LogsController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
end end
class Admin::ProjectsController < ApplicationController class Admin::ProjectsController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update] before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update]
def index def index
......
class Admin::ResqueController < ApplicationController class Admin::ResqueController < AdminController
layout 'admin'
def show def show
end end
end end
class Admin::TeamMembersController < ApplicationController class Admin::TeamMembersController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def edit def edit
@admin_team_member = UsersProject.find(params[:id]) @admin_team_member = UsersProject.find(params[:id])
end end
......
class Admin::UsersController < ApplicationController class Admin::UsersController < AdminController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index def index
@admin_users = User.scoped @admin_users = User.scoped
@admin_users = @admin_users.filter(params[:filter]) @admin_users = @admin_users.filter(params[:filter])
......
# Provides a base class for Admin controllers to subclass
#
# Automatically sets the layout and ensures an administrator is logged in
class AdminController < ApplicationController
layout 'admin'
before_filter :authenticate_admin!
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
end
...@@ -84,10 +84,6 @@ class ApplicationController < ActionController::Base ...@@ -84,10 +84,6 @@ class ApplicationController < ActionController::Base
abilities << Ability abilities << Ability
end end
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_project!(action) def authorize_project!(action)
return access_denied! unless can?(current_user, action, project) return access_denied! unless can?(current_user, action, project)
end end
......
...@@ -64,7 +64,7 @@ class CommitsController < ApplicationController ...@@ -64,7 +64,7 @@ class CommitsController < ApplicationController
@commit.to_patch, @commit.to_patch,
type: "text/plain", type: "text/plain",
disposition: 'attachment', disposition: 'attachment',
filename: "#{@commit.id.patch}" filename: "#{@commit.id}.patch"
) )
end end
......
...@@ -17,7 +17,7 @@ class IssuesController < ApplicationController ...@@ -17,7 +17,7 @@ class IssuesController < ApplicationController
before_filter :authorize_write_issue!, only: [:new, :create] before_filter :authorize_write_issue!, only: [:new, :create]
# Allow modify issue # Allow modify issue
before_filter :authorize_modify_issue!, only: [:close, :edit, :update] before_filter :authorize_modify_issue!, only: [:edit, :update]
# Allow destroy issue # Allow destroy issue
before_filter :authorize_admin_issue!, only: [:destroy] before_filter :authorize_admin_issue!, only: [:destroy]
...@@ -87,8 +87,6 @@ class IssuesController < ApplicationController ...@@ -87,8 +87,6 @@ class IssuesController < ApplicationController
end end
def destroy def destroy
return access_denied! unless can?(current_user, :admin_issue, @issue)
@issue.destroy @issue.destroy
respond_to do |format| respond_to do |format|
......
class OmniauthCallbacksController < Devise::OmniauthCallbacksController class OmniauthCallbacksController < Devise::OmniauthCallbacksController
Gitlab.config.omniauth_providers.each do |provider|
define_method provider['name'] do
handle_omniauth
end
end
# Extend the standard message generation to accept our custom exception # Extend the standard message generation to accept our custom exception
def failure_message def failure_message
...@@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
sign_in_and_redirect @user sign_in_and_redirect @user
end end
private
def handle_omniauth
oauth = request.env['omniauth.auth']
provider, uid = oauth['provider'], oauth['uid']
if current_user
# Change a logged-in user's authentication method:
current_user.extern_uid = uid
current_user.provider = provider
current_user.save
redirect_to profile_path
else
@user = User.find_or_new_for_omniauth(oauth)
if @user
sign_in_and_redirect @user
else
flash[:notice] = "There's no such user!"
redirect_to new_user_session_path
end
end
end
end end
...@@ -16,9 +16,6 @@ class ProfileController < ApplicationController ...@@ -16,9 +16,6 @@ class ProfileController < ApplicationController
def token def token
end end
def password
end
def password_update def password_update
params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"} params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
...@@ -32,7 +29,11 @@ class ProfileController < ApplicationController ...@@ -32,7 +29,11 @@ class ProfileController < ApplicationController
def reset_private_token def reset_private_token
current_user.reset_authentication_token! current_user.reset_authentication_token!
redirect_to profile_token_path redirect_to profile_account_path
end
def history
@events = current_user.recent_events.page(params[:page]).per(20)
end end
private private
......
...@@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController ...@@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController
# Authorize # Authorize
before_filter :add_project_abilities before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_admin_project!, except: [:show] before_filter :authorize_admin_project!, except: [:index, :show]
def index
end
def show def show
@team_member = project.users_projects.find(params[:id]) @team_member = project.users_projects.find(params[:id])
...@@ -22,7 +25,7 @@ class TeamMembersController < ApplicationController ...@@ -22,7 +25,7 @@ class TeamMembersController < ApplicationController
params[:project_access] params[:project_access]
) )
redirect_to team_project_path(@project) redirect_to project_team_index_path(@project)
end end
def update def update
...@@ -32,7 +35,7 @@ class TeamMembersController < ApplicationController ...@@ -32,7 +35,7 @@ class TeamMembersController < ApplicationController
unless @team_member.valid? unless @team_member.valid?
flash[:alert] = "User should have at least one role" flash[:alert] = "User should have at least one role"
end end
redirect_to team_project_path(@project) redirect_to project_team_index_path(@project)
end end
def destroy def destroy
...@@ -40,7 +43,7 @@ class TeamMembersController < ApplicationController ...@@ -40,7 +43,7 @@ class TeamMembersController < ApplicationController
@team_member.destroy @team_member.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to team_project_path(@project) } format.html { redirect_to project_team_index_path(@project) }
format.js { render nothing: true } format.js { render nothing: true }
end end
end end
......
...@@ -16,7 +16,7 @@ class CommitDecorator < ApplicationDecorator ...@@ -16,7 +16,7 @@ class CommitDecorator < ApplicationDecorator
# In case this first line is longer than 80 characters, it is cut off # In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended. # after 70 characters and ellipses (`&hellp;`) are appended.
def title def title
return no_commit_message unless safe_message return no_commit_message if safe_message.blank?
title_end = safe_message.index(/\n/) title_end = safe_message.index(/\n/)
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80) if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
......
...@@ -62,7 +62,7 @@ module ApplicationHelper ...@@ -62,7 +62,7 @@ module ApplicationHelper
{ label: "#{@project.name} / Wall", url: wall_project_path(@project) }, { label: "#{@project.name} / Wall", url: wall_project_path(@project) },
{ label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) }, { label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) },
{ label: "#{@project.name} / Commits", url: project_commits_path(@project) }, { label: "#{@project.name} / Commits", url: project_commits_path(@project) },
{ label: "#{@project.name} / Team", url: team_project_path(@project) } { label: "#{@project.name} / Team", url: project_team_index_path(@project) }
] ]
end end
...@@ -104,7 +104,8 @@ module ApplicationHelper ...@@ -104,7 +104,8 @@ module ApplicationHelper
# Profile Area # Profile Area
when :profile; current_page?(controller: "profile", action: :show) when :profile; current_page?(controller: "profile", action: :show)
when :password; current_page?(controller: "profile", action: :password) when :history; current_page?(controller: "profile", action: :history)
when :account; current_page?(controller: "profile", action: :account)
when :token; current_page?(controller: "profile", action: :token) when :token; current_page?(controller: "profile", action: :token)
when :design; current_page?(controller: "profile", action: :design) when :design; current_page?(controller: "profile", action: :design)
when :ssh_keys; controller.controller_name == "keys" when :ssh_keys; controller.controller_name == "keys"
...@@ -135,4 +136,10 @@ module ApplicationHelper ...@@ -135,4 +136,10 @@ module ApplicationHelper
"Never" "Never"
end end
end end
def authbutton(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag("authbuttons/#{file_name}",
alt: "Sign in with #{provider.to_s.titleize}")
end
end end
...@@ -11,7 +11,9 @@ module GitlabMarkdownHelper ...@@ -11,7 +11,9 @@ module GitlabMarkdownHelper
# explicitly produce the correct linking behavior (i.e. # explicitly produce the correct linking behavior (i.e.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>"). # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
def link_to_gfm(body, url, html_options = {}) def link_to_gfm(body, url, html_options = {})
gfm_body = gfm(body, html_options) return "" if body.blank?
gfm_body = gfm(escape_once(body), html_options)
gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match| gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
"</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1 "</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
......
module NotesHelper
def loading_more_notes?
params[:loading_more].present?
end
def loading_new_notes?
params[:loading_new].present?
end
def note_vote_class(note)
if note.upvote?
"vote upvote"
elsif note.downvote?
"vote downvote"
end
end
end
module ProfileHelper
def oauth_active_class provider
if current_user.provider == provider.to_s
'active'
end
end
end
...@@ -2,5 +2,9 @@ module ProjectsHelper ...@@ -2,5 +2,9 @@ module ProjectsHelper
def grouper_project_members(project) def grouper_project_members(project)
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access) @project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
end end
def remove_from_team_message(project, member)
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
end
end end
...@@ -8,7 +8,7 @@ module TabHelper ...@@ -8,7 +8,7 @@ module TabHelper
end end
def project_tab_class def project_tab_class
[:show, :files, :team, :edit, :update].each do |action| [:show, :files, :edit, :update].each do |action|
return "current" if current_page?(controller: "projects", action: action, id: @project) return "current" if current_page?(controller: "projects", action: action, id: @project)
end end
......
...@@ -18,6 +18,7 @@ module TreeHelper ...@@ -18,6 +18,7 @@ module TreeHelper
end end
def tree_full_path(content) def tree_full_path(content)
content.name.force_encoding('utf-8')
if params[:path] if params[:path]
File.join(params[:path], content.name) File.join(params[:path], content.name)
else else
......
...@@ -42,6 +42,14 @@ class Event < ActiveRecord::Base ...@@ -42,6 +42,14 @@ class Event < ActiveRecord::Base
push? || issue? || merge_request? || membership_changed? push? || issue? || merge_request? || membership_changed?
end end
def project_name
if project
project.name
else
"(deleted)"
end
end
def push? def push?
action == self.class::Pushed && valid_push? action == self.class::Pushed && valid_push?
end end
......
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Upvote include Votes
acts_as_taggable_on :labels acts_as_taggable_on :labels
......
...@@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit") ...@@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Upvote include Votes
BROKEN_DIFF = "--broken-diff" BROKEN_DIFF = "--broken-diff"
......
...@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base ...@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base
scope :today, where("created_at >= :date", date: Date.today) scope :today, where("created_at >= :date", date: Date.today)
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days)) scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
scope :since, lambda { |day| where("created_at >= :date", date: (day)) } scope :since, lambda { |day| where("created_at >= :date", date: (day)) }
scope :fresh, order("created_at DESC") scope :fresh, order("created_at ASC, id ASC")
scope :inc_author_project, includes(:project, :author) scope :inc_author_project, includes(:project, :author)
scope :inc_author, includes(:author) scope :inc_author, includes(:author)
...@@ -105,6 +105,12 @@ class Note < ActiveRecord::Base ...@@ -105,6 +105,12 @@ class Note < ActiveRecord::Base
def upvote? def upvote?
note.start_with?('+1') || note.start_with?(':+1:') note.start_with?('+1') || note.start_with?(':+1:')
end end
# Returns true if this is a downvote note,
# otherwise false is returned
def downvote?
note.start_with?('-1') || note.start_with?(':-1:')
end
end end
# == Schema Information # == Schema Information
# #
......
...@@ -171,6 +171,10 @@ class Project < ActiveRecord::Base ...@@ -171,6 +171,10 @@ class Project < ActiveRecord::Base
end end
end end
def wiki_notes
Note.where(noteable_id: wikis.map(&:id), noteable_type: 'Wiki', project_id: self.id)
end
def project_id def project_id
self.id self.id
end end
......
...@@ -16,7 +16,7 @@ class Tree ...@@ -16,7 +16,7 @@ class Tree
def initialize(raw_tree, project, ref = nil, path = nil) def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path, @project, @ref, @path = project, ref, path,
@tree = if path @tree = if path
raw_tree / path raw_tree / path.dup.force_encoding('ascii-8bit')
else else
raw_tree raw_tree
end end
......
...@@ -86,33 +86,20 @@ class User < ActiveRecord::Base ...@@ -86,33 +86,20 @@ class User < ActiveRecord::Base
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end end
def self.find_for_ldap_auth(auth, signed_in_resource=nil) def self.create_from_omniauth(auth, ldap = false)
uid = auth.info.uid gitlab_auth.create_from_omniauth(auth, ldap)
provider = auth.provider end
name = auth.info.name.force_encoding("utf-8")
email = auth.info.email.downcase unless auth.info.email.nil? def self.find_or_new_for_omniauth(auth)
raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil? gitlab_auth.find_or_new_for_omniauth(auth)
if @user = User.find_by_extern_uid_and_provider(uid, provider)
@user
# workaround for backward compatibility
elsif @user = User.find_by_email(email)
logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"
@user.update_attributes(:extern_uid => uid, :provider => provider)
@user
else
logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}"
password = Devise.friendly_token[0, 8].downcase
@user = User.create(
:extern_uid => uid,
:provider => provider,
:name => name,
:email => email,
:password => password,
:password_confirmation => password,
:projects_limit => Gitlab.config.default_projects_limit
)
end end
def self.find_for_ldap_auth(auth, signed_in_resource = nil)
gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
end
def self.gitlab_auth
Gitlab::Auth.new
end end
def self.search query def self.search query
...@@ -148,4 +135,3 @@ end ...@@ -148,4 +135,3 @@ end
# bio :string(255) # bio :string(255)
# blocked :boolean(1) default(FALSE), not null # blocked :boolean(1) default(FALSE), not null
# #
...@@ -28,7 +28,6 @@ class Wiki < ActiveRecord::Base ...@@ -28,7 +28,6 @@ class Wiki < ActiveRecord::Base
end end
new_wiki new_wiki
end end
end end
end end
# == Schema Information # == Schema Information
......
...@@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer ...@@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer
end end
def after_destroy(project) def after_destroy(project)
log_info("Project \"#{project.name}\" was removed")
project.destroy_repository project.destroy_repository
end end
def after_create project
log_info("#{project.owner.name} created a new project \"#{project.name}\"")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end end
class UserObserver < ActiveRecord::Observer class UserObserver < ActiveRecord::Observer
def after_create(user) def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
Notify.new_user_email(user.id, user.password).deliver Notify.new_user_email(user.id, user.password).deliver
end end
def after_destroy user
log_info("User \"#{user.name}\" (#{user.email}) was removed")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end end
module Upvote
# Return the number of +1 comments (upvotes)
def upvotes
notes.select(&:upvote?).size
end
end
module Votes
# Return the number of +1 comments (upvotes)
def upvotes
notes.select(&:upvote?).size
end
def upvotes_in_percent
if votes_count.zero?
0
else
100.0 / votes_count * upvotes
end
end
# Return the number of -1 comments (downvotes)
def downvotes
notes.select(&:downvote?).size
end
def downvotes_in_percent
if votes_count.zero?
0
else
100.0 - upvotes_in_percent
end
end
# Return the total number of votes
def votes_count
upvotes + downvotes
end
end
.file_holder#README %ul.nav.nav-tabs.log-tabs
%li.active
= link_to "githost.log", "#githost", 'data-toggle' => 'tab'
%li
= link_to "application.log", "#application", 'data-toggle' => 'tab'
.tab-content
.tab-pane.active#githost
.file_holder#README
.file_title .file_title
%i.icon-file %i.icon-file
githost.log githost.log
.file_content.logs .file_content.logs
%ol %ol
- Gitlab::Logger.read_latest.each do |line| - Gitlab::GitLogger.read_latest.each do |line|
%li
%p= line
.tab-pane#application
.file_holder#README
.file_title
%i.icon-file
application.log
.file_content.logs
%ol
- Gitlab::AppLogger.read_latest.each do |line|
%li %li
%p= line %p= line
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
- unless project.new_record? - unless project.new_record?
.clearfix .clearfix
= f.label :owner_id = f.label :owner_id
.input= f.select :owner_id, User.all.map { |user| [user.name, user.id] } .input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
- if project.repo_exists? - if project.repo_exists?
.clearfix .clearfix
...@@ -69,7 +69,6 @@ ...@@ -69,7 +69,6 @@
:javascript :javascript
$(function(){ $(function(){
$('#project_owner_id').chosen();
new Projects(); new Projects();
}) })
...@@ -71,25 +71,11 @@ ...@@ -71,25 +71,11 @@
%th Project Access: %th Project Access:
%tr %tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
%tr %tr
%td= submit_tag 'Add', class: "btn primary" %td= submit_tag 'Add', class: "btn primary"
%td %td
Read more about project permissions Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink" %strong= link_to "here", help_permissions_path, class: "vlink"
:css
form select {
width:150px;
}
#user_ids {
width:300px;
}
:javascript
$('select#user_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();
%h3 Resque %h3.page_title Resque
%iframe{src: resque_url, width: 1168, height: 600, style: "border: none"} %br
.ui-box
%iframe{src: resque_url, width: '100%', height: 600, style: "border: none"}
...@@ -8,20 +8,9 @@ ...@@ -8,20 +8,9 @@
.clearfix .clearfix
%label Project Access: %label Project Access:
.input .input
= f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select" = f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
%br %br
.actions .actions
= f.submit 'Save', class: "btn primary" = f.submit 'Save', class: "btn primary"
= link_to 'Cancel', :back, class: "btn" = link_to 'Cancel', :back, class: "btn"
:css
form select {
width:300px;
}
:javascript
$('select#team_member_user_id').chosen();
$('select#team_member_project_id').chosen();
$('select#team_member_repo_access').chosen();
$('select#team_member_project_access').chosen();
...@@ -68,8 +68,8 @@ ...@@ -68,8 +68,8 @@
%th Project Access: %th Project Access:
%tr %tr
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select" %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
%tr %tr
%td= submit_tag 'Add', class: "btn primary" %td= submit_tag 'Add', class: "btn primary"
...@@ -97,17 +97,3 @@ ...@@ -97,17 +97,3 @@
%td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
:css
form select {
width:150px;
}
#project_ids {
width:300px;
}
:javascript
$('select#project_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do = link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
%strong Browse Code » %strong Browse Code »
%h3.commit-title.page_title %h3.commit-title.page_title
= gfm @commit.title = gfm escape_once(@commit.title)
- if @commit.description.present? - if @commit.description.present?
%pre.commit-description %pre.commit-description
= gfm @commit.description = gfm escape_once(@commit.description)
.commit-info .commit-info
.row .row
.span4 .span4
......
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select"
= hidden_field_tag :destination, "commits"
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"} %li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
= link_to project_commits_path(@project) do = link_to project_commits_path(@project) do
Commits Commits
...@@ -20,14 +16,8 @@ ...@@ -20,14 +16,8 @@
Tags Tags
%span.badge= @project.repo.tag_count %span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token - if current_page?(project_commits_path(@project)) && current_user.private_token
%li.right %li.right
%span.rss-icon %span.rss-icon
= link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do = link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do
= image_tag "rss_ui.png", title: "feed" = image_tag "rss_ui.png", title: "feed"
:javascript
$(function(){
$('.project-refs-select').chosen();
});
...@@ -13,14 +13,11 @@ ...@@ -13,14 +13,11 @@
%td.old_line %td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- if @comments_allowed - if @comments_allowed
= link_to "", "#", class: "line_note_link", "line_code" => line_code, title: "Add note for this line" = render "notes/per_line_note_link", line_code: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;" %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
- if @comments_allowed - if @comments_allowed
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at).reverse - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
- unless comments.empty? - unless comments.empty?
- comments.each_with_index do |note, i| = render "notes/per_line_notes_with_reply", notes: comments
= render "notes/reply_button", line_code: line_code if i.zero?
= render "notes/per_line_show", note: note
- @line_notes.reject!{ |n| n == note }
= render "commits/commit_box" = render "commits/commit_box"
= render "commits/diffs", diffs: @commit.diffs = render "commits/diffs", diffs: @commit.diffs
= render "notes/notes", tid: @commit.id, tt: "commit" = render "notes/notes_with_form", tid: @commit.id, tt: "commit"
= render "notes/per_line_form" = render "notes/per_line_form"
......
...@@ -31,13 +31,19 @@ ...@@ -31,13 +31,19 @@
%span= project_last_activity(project) %span= project_last_activity(project)
.bottom= paginate @projects, theme: "gitlab" .bottom= paginate @projects, theme: "gitlab"
%hr
%div %div
%span.rss-icon %span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
= image_tag "rss_ui.png", title: "feed" = image_tag "rss_ui.png", title: "feed"
%strong News Feed %strong News Feed
%hr
.gitlab-promo
= link_to "Homepage", "http://gitlabhq.com"
= link_to "Blog", "http://blog.gitlabhq.com"
= link_to "@gitlabhq", "https://twitter.com/gitlabhq"
- else - else
%h3.nothing_here_message There are no projects you have access to. %h3.nothing_here_message There are no projects you have access to.
%br %br
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
$(function() { $(function() {
$('#new_user').toggle(); $('#new_user').toggle();
}); });
= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| = form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
= f.text_field :email, :class => "text top", :placeholder => "Email" = f.text_field :email, :class => "text top", :placeholder => "Email"
= f.password_field :password, :class => "text bottom", :placeholder => "Password" = f.password_field :password, :class => "text bottom", :placeholder => "Password"
- if devise_mapping.rememberable? - if devise_mapping.rememberable?
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.right .right
= render :partial => "devise/shared/links" = render :partial => "devise/shared/links"
- if devise_mapping.omniauthable? - if devise_mapping.omniauthable?
- resource_class.omniauth_providers.each do |provider|
%hr/ %hr/
= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" - resource_class.omniauth_providers.each do |provider|
%br/ %span
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
%strong.cdark= commit.author_name %strong.cdark= commit.author_name
&ndash; &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm truncate(commit.title, length: 50) rescue "--broken encoding" = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%strong #{event.author_name} %strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name %span.event_label{class: event.action_name}= event.action_name
project project
%strong= link_to event.project.name, event.project %strong= link_to event.project_name, event.project
%span.cgray %span.cgray
= time_ago_in_words(event.created_at) = time_ago_in_words(event.created_at)
ago. ago.
......
...@@ -18,12 +18,12 @@ ...@@ -18,12 +18,12 @@
= f.label :assignee_id do = f.label :assignee_id do
%i.icon-user %i.icon-user
Assign to Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }) .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
.issue_milestone .issue_milestone
= f.label :milestone_id do = f.label :milestone_id do
%i.icon-time %i.icon-time
Milestone Milestone
.input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }) .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
.issue_description .issue_description
.clearfix .clearfix
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
.right .right
- issue.labels.each do |label| - issue.labels.each do |label|
%span.label.label-issue.grouped %span.label.label-tag.grouped
%i.icon-tag %i.icon-tag
= label.name = label.name
- if issue.notes.any? - if issue.notes.any?
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
- else - else
&nbsp; &nbsp;
- if issue.upvotes > 0 - if issue.votes_count > 0
%span.badge.badge-success= "+#{issue.upvotes}" = render 'votes/votes_inline', votable: issue
= render "form" = render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
});
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do = form_tag search_project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: :right do
= hidden_field_tag :project_id, @project.id, { id: 'project_id' } = hidden_field_tag :project_id, @project.id, { id: 'project_id' }
= hidden_field_tag :status, params[:f] = hidden_field_tag :status, params[:f]
= search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib' } = search_field_tag :issue_search, nil, { placeholder: 'Search', class: 'issue_search span3 right neib search-text-input' }
.clearfix .clearfix
......
= render "form" = render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
});
...@@ -8,22 +8,22 @@ ...@@ -8,22 +8,22 @@
%span.right %span.right
- if can?(current_user, :admin_project, @project) || @issue.author == current_user - if can?(current_user, :admin_project, @project) || @issue.author == current_user
- if @issue.closed - if @issue.closed
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn small" = link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped success"
- else - else
= link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn small", title: "Close Issue" = link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped danger", title: "Close Issue"
- if can?(current_user, :admin_project, @project) || @issue.author == current_user - if can?(current_user, :admin_project, @project) || @issue.author == current_user
= link_to edit_project_issue_path(@project, @issue), class: "btn small" do = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
%i.icon-edit %i.icon-edit
Edit Edit
%br .right
- if @issue.upvotes > 0 .span3#votes= render 'votes/votes_block', votable: @issue
.upvotes#upvotes= "+#{pluralize @issue.upvotes, 'upvote'}"
.back_link .back_link
= link_to project_issues_path(@project) do = link_to project_issues_path(@project) do
&larr; To issues list &larr; To issues list
.main_box .main_box
.top_box_content .top_box_content
%h4 %h4
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
.alert-message.error.status_info Closed .alert-message.error.status_info Closed
- else - else
.alert-message.success.status_info Open .alert-message.success.status_info Open
= gfm @issue.title = gfm escape_once(@issue.title)
.middle_box_content .middle_box_content
%cite.cgray Created by %cite.cgray Created by
...@@ -61,4 +61,4 @@ ...@@ -61,4 +61,4 @@
= markdown @issue.description = markdown @issue.description
.issue_notes#notes= render "notes/notes", tid: @issue.id, tt: "issue" .issue_notes.voting_notes#notes= render "notes/notes_with_form", tid: @issue.id, tt: "issue"
%li.wll %li.wll
%strong= label.name %strong
%i.icon-tag
= label.name
.right .right
%span= pluralize label.count, 'issue' = link_to project_issues_path(label_name: label.name) do
%strong
= pluralize(label.count, 'issue')
= "»"
...@@ -9,20 +9,20 @@ ...@@ -9,20 +9,20 @@
%li.home{class: tab_class(:profile)} %li.home{class: tab_class(:profile)}
= link_to "Profile", profile_path = link_to "Profile", profile_path
%li{class: tab_class(:password)} %li{class: tab_class(:account)}
= link_to "Password", profile_password_path = link_to "Account", profile_account_path
%li{class: tab_class(:ssh_keys)} %li{class: tab_class(:ssh_keys)}
= link_to keys_path do = link_to keys_path do
SSH Keys SSH Keys
%span.count= current_user.keys.count %span.count= current_user.keys.count
%li{class: tab_class(:token)}
= link_to "Token", profile_token_path
%li{class: tab_class(:design)} %li{class: tab_class(:design)}
= link_to "Design", profile_design_path = link_to "Design", profile_design_path
%li{class: tab_class(:history)}
= link_to "History", profile_history_path
.content .content
= yield = yield
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.padded .padded
= f.label :source_branch, "From", class: "control-label" = f.label :source_branch, "From", class: "control-label"
.controls .controls
= f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") = f.select(:source_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'})
.mr_source_commit .mr_source_commit
.span2 .span2
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.padded .padded
= f.label :target_branch, "To", class: "control-label" = f.label :target_branch, "To", class: "control-label"
.controls .controls
= f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, style: "width:250px") = f.select(:target_branch, @project.heads.map(&:name), { include_blank: "Select branch" }, {class: 'chosen span3'})
.mr_target_commit .mr_target_commit
%h4.cdark 2. Fill info %h4.cdark 2. Fill info
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
= f.label :assignee_id do = f.label :assignee_id do
%i.icon-user %i.icon-user
Assign to Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, style: "width:250px") .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
.control-group .control-group
...@@ -56,18 +56,12 @@ ...@@ -56,18 +56,12 @@
= link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do = link_to project_merge_request_path(@project, @merge_request), class: "btn cancel-btn" do
Cancel Cancel
:javascript :javascript
$(function(){ $(function(){
disableButtonIfEmptyField("#merge_request_title", ".save-btn"); disableButtonIfEmptyField("#merge_request_title", ".save-btn");
$('select#merge_request_assignee_id').chosen();
$('select#merge_request_source_branch').chosen();
$('select#merge_request_target_branch').chosen();
var source_branch = $("#merge_request_source_branch"); var source_branch = $("#merge_request_source_branch");
var target_branch = $("#merge_request_target_branch"); var target_branch = $("#merge_request_target_branch");
$.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() }); $.get("#{branch_from_project_merge_requests_path(@project)}", {ref: source_branch.val() });
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() }); $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: target_branch.val() });
...@@ -79,4 +73,3 @@ ...@@ -79,4 +73,3 @@
$.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() }); $.get("#{branch_to_project_merge_requests_path(@project)}", {ref: $(this).val() });
}); });
}); });
...@@ -23,5 +23,6 @@ ...@@ -23,5 +23,6 @@
authored by #{merge_request.author_name} authored by #{merge_request.author_name}
= time_ago_in_words(merge_request.created_at) = time_ago_in_words(merge_request.created_at)
ago ago
- if merge_request.upvotes > 0
%span.badge.badge-success= "+#{merge_request.upvotes}" - if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
%i.icon-list-alt %i.icon-list-alt
Diff Diff
.merge_request_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } .merge_request_notes.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
= render("notes/notes", tid: @merge_request.id, tt: "merge_request") = render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")
.merge-request-diffs .merge-request-diffs
= render "merge_requests/show/diffs" if @diffs = render "merge_requests/show/diffs" if @diffs
.status .status
......
= render "show" = render "show"
:javascript
$(function(){
PerLineNotes.init();
});
:plain :plain
$(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}"); $(".merge-request-diffs").html("#{escape_javascript(render(partial: "merge_requests/show/diffs"))}");
$(function(){
PerLineNotes.init();
});
:plain :plain
$(".merge-request-notes").html("#{escape_javascript(render("notes/notes", tid: @merge_request.id, tt: "merge_request"))}"); $(".merge-request-notes").html("#{escape_javascript(render notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")}");
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
.alert-message.error.status_info Closed .alert-message.error.status_info Closed
- else - else
.alert-message.success.status_info Open .alert-message.success.status_info Open
= gfm @merge_request.title = gfm escape_once(@merge_request.title)
.middle_box_content .middle_box_content
%div %div
......
...@@ -23,10 +23,8 @@ ...@@ -23,10 +23,8 @@
%i.icon-edit %i.icon-edit
Edit Edit
%br .right
- if @merge_request.upvotes > 0 .span3#votes= render 'votes/votes_block', votable: @merge_request
.upvotes#upvotes= "+#{pluralize @merge_request.upvotes, 'upvote'}"
.back_link .back_link
= link_to project_merge_requests_path(@project) do = link_to project_merge_requests_path(@project) do
......
= render "form" = render "form"
:javascript
$(function(){
$('select#issue_assignee_id').chosen();
});
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
.alert-message.error.status_info Closed .alert-message.error.status_info Closed
- else - else
.alert-message.success.status_info Open .alert-message.success.status_info Open
= gfm @milestone.title = gfm escape_once(@milestone.title)
%small.right= @milestone.expires_at %small.right= @milestone.expires_at
.middle_box_content .middle_box_content
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
.clearfix .clearfix
.row.note_advanced_opts.hide .row.note_advanced_opts
.span3 .span3
= f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note" = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
= link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link' = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
$('.note-form-holder #preview-link').text('Preview'); $('.note-form-holder #preview-link').text('Preview');
$('.note-form-holder #preview-note').hide(); $('.note-form-holder #preview-note').hide();
$('.note-form-holder').show(); $('.note-form-holder').show();
NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}"); NoteList.appendNewNote(#{note.id}, "#{escape_javascript(render "notes/note", note: note)}");
- else - else
:plain :plain
$(".note-form-holder").replaceWith("#{escape_javascript(render('form'))}"); $(".note-form-holder").replaceWith("#{escape_javascript(render 'form')}");
- if note.valid?
:plain
$(".per_line_form").hide();
$('.line-note-form-holder textarea').val("");
$("a.line_note_reply_link[line_code='#{note.line_code}']").closest("tr").remove();
var trEl = $(".#{note.line_code}").parent();
trEl.after("#{escape_javascript(render partial: "notes/per_line_show", locals: {note: note})}");
trEl.after("#{escape_javascript(render partial: "notes/reply_button", locals: {line_code: note.line_code})}");
- if note.valid?
:plain
// hide and reset the form
$(".per_line_form").hide();
$('.line-note-form-holder textarea').val("");
// find the reply button for this line
// (might not be there if this is the first note)
var trRpl = $("a.line_note_reply_link[data-line-code='#{note.line_code}']").closest("tr");
if (trRpl.size() == 0) {
// find the commented line ...
var trEl = $(".#{note.line_code}").parent();
// ... and insert the note and the reply button after it
trEl.after("#{escape_javascript(render "notes/per_line_reply_button", line_code: note.line_code)}");
trEl.after("#{escape_javascript(render "notes/per_line_note", note: note)}");
} else {
// instert new note before reply button
trRpl.before("#{escape_javascript(render "notes/per_line_note", note: note)}");
}
- unless @notes.blank?
- if params[:last_id]
:plain
NoteList.replace("#{escape_javascript(render(partial: 'notes/notes_list'))}");
- elsif params[:first_id]
:plain
NoteList.append(#{@notes.last.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
- else
:plain
NoteList.setContent(#{@notes.last.id}, #{@notes.first.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
- else
- if params[:first_id]
:plain
NoteList.append(#{params[:first_id]}, "");
%li{id: dom_id(note), class: "note"} %li{id: dom_id(note), class: "note #{note_vote_class(note)}"}
= image_tag gravatar_icon(note.author.email), class: "avatar s32" = image_tag gravatar_icon(note.author.email), class: "avatar s32"
%div.note-author %div.note-author
%strong= note.author_name %strong= note.author_name
...@@ -6,6 +6,14 @@ ...@@ -6,6 +6,14 @@
%cite.cgray %cite.cgray
= time_ago_in_words(note.updated_at) = time_ago_in_words(note.updated_at)
ago ago
- if note.upvote?
%span.label.label-success
%i.icon-thumbs-up
\+1
- if note.downvote?
%span.label.label-error
%i.icon-thumbs-down
\-1
- if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
= link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do = link_to [@project, note], confirm: 'Are you sure?', method: :delete, remote: true, class: "cred delete-note btn very_small" do
%i.icon-trash %i.icon-trash
......
- if can? current_user, :write_note, @project - @notes.each do |note|
= render "notes/form" - next unless note.author
.clear = render "note", note: note
%hr
%ul#new_notes_list
%ul#notes-list
.status
:javascript
$(function(){
NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
});
- @notes.each do |note|
- next unless note.author
= render partial: "notes/show", locals: {note: note}
%ul#notes-list
%ul#new-notes-list
.notes-status
- if can? current_user, :write_note, @project
= render "notes/common_form"
:javascript
$(function(){
NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
});
%tr.line_notes_row %tr.line_notes_row
%td{colspan: 3} %td{colspan: 3}
%ul %ul
= render partial: "notes/show", locals: {note: note} = render "notes/note", note: note
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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