Commit 6dd47c59 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into stable

parents 4a1b42d2 4afb7b7c

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

...@@ -22,4 +22,4 @@ config/unicorn.rb ...@@ -22,4 +22,4 @@ config/unicorn.rb
db/data.yml db/data.yml
.idea .idea
.DS_Store .DS_Store
.chef
...@@ -9,6 +9,8 @@ branches: ...@@ -9,6 +9,8 @@ branches:
- 'master' - 'master'
rvm: rvm:
- 1.9.3 - 1.9.3
services:
- mysql
before_script: before_script:
- "cp config/database.yml.$DB config/database.yml" - "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml" - "cp config/gitlab.yml.example config/gitlab.yml"
......
v 2.9.0
- fixed inline notes bugs
- refactored rspecs
- refactored gitolite backend
- added factory_girl
- restyled projects list on dashboard
- ssh keys validation to prevent gitolite crash
- send notifications if changed premission in project
- scss refactoring. gitlab_bootstrap/ dir
- fix git push http body bigger than 112k problem
- list of labels page under issues tab
- API for milestones, keys
- restyled buttons
- OAuth
- Comment order changed
v 2.8.1 v 2.8.1
- ability to disable gravatars - ability to disable gravatars
- improved MR diff logic - improved MR diff logic
......
## 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"
...@@ -45,6 +57,7 @@ gem "seed-fu" ...@@ -45,6 +57,7 @@ gem "seed-fu"
# Markdown to HTML # Markdown to HTML
gem "redcarpet", "~> 2.1.1" gem "redcarpet", "~> 2.1.1"
gem "github-markup", "~> 0.7.4"
# Servers # Servers
gem "thin" gem "thin"
...@@ -97,20 +110,28 @@ group :development do ...@@ -97,20 +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'
# 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'
...@@ -119,5 +140,5 @@ group :test do ...@@ -119,5 +140,5 @@ group :test do
end end
group :production do group :production do
gem "gitlab_meta", '2.8' gem "gitlab_meta", '2.9'
end end
...@@ -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,15 +99,11 @@ GEM ...@@ -100,15 +99,11 @@ 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)
bootstrap-sass (2.0.4.0) bootstrap-sass (2.0.4.0)
builder (3.0.0) builder (3.0.2)
capybara (1.1.2) capybara (1.1.2)
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
...@@ -125,7 +120,7 @@ GEM ...@@ -125,7 +120,7 @@ GEM
charlock_holmes (0.6.8) charlock_holmes (0.6.8)
childprocess (0.3.2) childprocess (0.3.2)
ffi (~> 1.0.6) ffi (~> 1.0.6)
chosen-rails (0.9.8) chosen-rails (0.9.8.3)
railties (~> 3.0) railties (~> 3.0)
thor (~> 0.14) thor (~> 0.14)
coderay (1.0.6) coderay (1.0.6)
...@@ -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)
...@@ -166,20 +153,36 @@ GEM ...@@ -166,20 +153,36 @@ GEM
eventmachine (0.12.10) eventmachine (0.12.10)
execjs (1.4.0) execjs (1.4.0)
multi_json (~> 1.0) multi_json (~> 1.0)
factory_girl (4.0.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.0.0)
factory_girl (~> 4.0.0)
railties (>= 3.0.0)
faraday (0.8.4)
multipart-post (~> 1.1)
ffaker (1.14.0) 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)
gitlab_meta (2.8) github-markup (0.7.4)
gitlab_meta (2.9)
grape (0.2.1) grape (0.2.1)
hashie (~> 1.2) hashie (~> 1.2)
multi_json multi_json
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)
...@@ -193,7 +196,8 @@ GEM ...@@ -193,7 +196,8 @@ GEM
httparty (0.8.3) httparty (0.8.3)
multi_json (~> 1.0) multi_json (~> 1.0)
multi_xml multi_xml
i18n (0.6.0) httpauth (0.1)
i18n (0.6.1)
journey (1.0.4) journey (1.0.4)
jquery-rails (2.0.2) jquery-rails (2.0.2)
railties (>= 3.2.0, < 5.0) railties (>= 3.2.0, < 5.0)
...@@ -201,11 +205,12 @@ GEM ...@@ -201,11 +205,12 @@ GEM
jquery-ui-rails (0.5.0) jquery-ui-rails (0.5.0)
jquery-rails jquery-rails
railties (>= 3.1.0) railties (>= 3.1.0)
json (1.7.4) json (1.7.5)
kaminari (0.13.0) jwt (0.1.5)
multi_json (>= 1.0)
kaminari (0.14.0)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
railties (>= 3.0.0)
kgio (2.7.4) kgio (2.7.4)
launchy (2.1.0) launchy (2.1.0)
addressable (~> 2.2.6) addressable (~> 2.2.6)
...@@ -214,6 +219,7 @@ GEM ...@@ -214,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)
...@@ -224,12 +230,35 @@ GEM ...@@ -224,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)
...@@ -269,6 +298,9 @@ GEM ...@@ -269,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)
...@@ -319,7 +351,7 @@ GEM ...@@ -319,7 +351,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
rubyzip rubyzip
settingslogic (2.0.8) settingslogic (2.0.8)
shoulda-matchers (1.1.0) shoulda-matchers (1.3.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
simplecov (0.6.4) simplecov (0.6.4)
multi_json (~> 1.0) multi_json (~> 1.0)
...@@ -331,6 +363,13 @@ GEM ...@@ -331,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)
...@@ -343,7 +382,7 @@ GEM ...@@ -343,7 +382,7 @@ GEM
daemons (>= 1.0.9) daemons (>= 1.0.9)
eventmachine (>= 0.12.6) eventmachine (>= 0.12.6)
rack (>= 1.0.0) rack (>= 1.0.0)
thor (0.15.4) thor (0.16.0)
tilt (1.3.3) tilt (1.3.3)
treetop (1.4.10) treetop (1.4.10)
polyglot polyglot
...@@ -372,8 +411,6 @@ PLATFORMS ...@@ -372,8 +411,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
...@@ -383,19 +420,23 @@ DEPENDENCIES ...@@ -383,19 +420,23 @@ 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
email_spec email_spec
factory_girl_rails
ffaker ffaker
foreman foreman
git git
gitlab_meta (= 2.8) github-markup (~> 0.7.4)
gitlab_meta (= 2.9)
gitolite! gitolite!
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
...@@ -407,12 +448,18 @@ DEPENDENCIES ...@@ -407,12 +448,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
...@@ -424,6 +471,7 @@ DEPENDENCIES ...@@ -424,6 +471,7 @@ DEPENDENCIES
shoulda-matchers shoulda-matchers
simplecov simplecov
six six
spinach-rails
sqlite3 sqlite3
stamp stamp
therubyracer therubyracer
......
# 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(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{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
...@@ -39,5 +39,6 @@ Email ...@@ -39,5 +39,6 @@ Email
## Contribute ## Contribute
[Development Tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
Want to help - send a pull request. Want to help - send a pull request.
We'll accept good pull requests. We'll accept good pull requests.
app/assets/images/file_dir.png

517 Bytes | W: | H:

app/assets/images/file_dir.png

1.61 KB | W: | H:

app/assets/images/file_dir.png
app/assets/images/file_dir.png
app/assets/images/file_dir.png
app/assets/images/file_dir.png
  • 2-up
  • Swipe
  • Onion skin
$(document).ready(function(){
$('input#user_force_random_password').on('change', function(elem) {
var elems = $('#user_password, #user_password_confirmation');
if ($(this).attr('checked')) {
elems.val('').attr('disabled', true);
} else {
elems.removeAttr('disabled');
}
});
});
$ ->
$('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation')
if $(@).attr 'checked'
elems.val('').attr 'disabled', true
else
elems.removeAttr 'disabled'
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
...@@ -11,120 +11,9 @@ ...@@ -11,120 +11,9 @@
//= 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
//= require branch-graph //= require branch-graph
//= require_tree . //= require_tree .
$(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"});
}
function initGraphNav() {
$(".graph svg").css("position", "relative");
$("body").bind("keyup", function(e) {
if(e.keyCode == 37) { // left
$(".graph svg").animate({ left: "+=400" });
} else if(e.keyCode == 39) { // right
$(".graph svg").animate({ left: "-=400" });
}
});
}
initGraphNav = ->
$('.graph svg').css 'position', 'relative'
$('body').bind 'keyup', (e) ->
if e.keyCode is 37 # left
$('.graph svg').animate left: '+=400'
else if e.keyCode is 39 # right
$('.graph svg').animate left: '-=400'
window.initGraphNav = initGraphNav
...@@ -5,6 +5,7 @@ function switchToNewIssue(form){ ...@@ -5,6 +5,7 @@ function switchToNewIssue(form){
$('select#issue_milestone_id').chosen(); $('select#issue_milestone_id').chosen();
$("#new_issue_dialog").show("fade", { direction: "right" }, 150); $("#new_issue_dialog").show("fade", { direction: "right" }, 150);
$('.top-tabs .add_new').hide(); $('.top-tabs .add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
}); });
} }
...@@ -15,6 +16,7 @@ function switchToEditIssue(form){ ...@@ -15,6 +16,7 @@ function switchToEditIssue(form){
$('select#issue_milestone_id').chosen(); $('select#issue_milestone_id').chosen();
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150); $("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
$('.add_new').hide(); $('.add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
}); });
} }
...@@ -78,6 +80,10 @@ function issuesPage(){ ...@@ -78,6 +80,10 @@ function issuesPage(){
$(this).closest("form").submit(); $(this).closest("form").submit();
}); });
$("#new_issue_link").click(function(){
updateNewIssueURL();
});
$('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){ $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
var t = $(this), var t = $(this),
totalIssues, totalIssues,
...@@ -124,3 +130,20 @@ function issuesCheckChanged() { ...@@ -124,3 +130,20 @@ function issuesCheckChanged() {
$('.issues_filters').show(); $('.issues_filters').show();
} }
} }
function updateNewIssueURL(){
var new_issue_link = $("#new_issue_link");
var milestone_id = $("#milestone_id").val();
var assignee_id = $("#assignee_id").val();
var new_href = "";
if(milestone_id){
new_href = "issue[milestone_id]=" + milestone_id + "&";
}
if(assignee_id){
new_href = new_href + "issue[assignee_id]=" + assignee_id;
}
if(new_href.length){
new_href = new_issue_link.attr("href") + "?" + new_href;
new_issue_link.attr("href", new_href);
}
};
var Loader = {
img_src: "/assets/ajax-loader.gif",
html:
function(width) {
img = $("<img>");
img.attr("width", width);
img.attr("src", this.img_src);
return img;
}
}
Loader =
html: (width) ->
$('<img>').attr src: '/assets/ajax-loader.gif', width: width
window.Loader = Loader
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)
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(); });
$("#new_note").live("ajax:before", function(){
$(".submit_note").attr("disabled", "disabled");
})
$("#new_note").live("ajax:complete", function(){
$(".submit_note").removeAttr("disabled");
})
$("#note_note").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 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(); });
}
});
}
}
function Projects() {
$("#project_name").live("change", function(){
var slug = slugify($(this).val());
$("#project_code").val(slug);
$("#project_path").val(slug);
});
$('.new_project, .edit_project').live('ajax:before', function() {
$('.project_new_holder, .project_edit_holder').hide();
$('.ajax_loader').show();
});
$('form #project_default_branch').chosen();
}
window.Projects = ->
$('#project_name').on 'change', ->
slug = slugify $(@).val()
$('#project_code, #project_path').val slug
$('.new_project, .edit_project').on 'ajax:before', ->
$('.project_new_holder, .project_edit_holder').hide()
$('.save-project-loader').show()
$('form #project_default_branch').chosen()
disableButtonIfEmptyField '#project_name', '.project-submit'
$ ->
# Git clone panel switcher
scope = $ '.project_clone_holder'
if scope.length > 0
$('a, button', scope).click ->
$('a, button', scope).removeClass 'active'
$(@).addClass 'active'
$('#project_clone', scope).val $(@).data 'clone'
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
$(document).ready(function(){
$("#snippets-table .snippet").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
});
$ ->
$('#snippets-table .snippet').live 'click', (e) ->
if e.target.nodeName isnt 'A' and e.target.nodeName isnt 'INPUT'
location.href = $(@).attr 'url'
e.stopPropagation()
false
function backToMembers(){
$("#new_team_member").hide("slide", { direction: "right" }, 150, function(){
$("#team-table").show("slide", { direction: "left" }, 150, function() {
$("#new_team_member").remove();
$(".add_new").show();
});
});
}
.diff_file_header a,
.file_stats a {
color:$style_color;
}
/** LAYOUT **/ /** LAYOUT **/
body {
margin-bottom:20px;
}
.container { .container {
padding-top:0; padding-top:0;
z-index:5; z-index:5;
...@@ -40,30 +38,6 @@ ...@@ -40,30 +38,6 @@
color: $link_color; color: $link_color;
} }
.widget {
@include shade;
padding:20px;
margin-bottom:20px;
border: 1px solid #DDD;
border-radius: 5px;
background:#fafafa;
.link_holder {
background:#eee;
position:relative;
left:-20px;
top:20px;
padding:10px 20px;
width:100%;
border-top:1px solid #ccc;
a {
font-size:14px;
color:#666;
}
}
}
.help li { color:#111 } .help li { color:#111 }
.back_link { .back_link {
...@@ -88,16 +62,6 @@ ...@@ -88,16 +62,6 @@
padding-left:20px; padding-left:20px;
} }
.number {
border-radius: 4px;
text-shadow: none;
background: rgba(0,0,0,.12);
text-align: center;
padding: 2px 4px;
line-height:18px;
margin-left:2px;
}
table a code { table a code {
position: relative; position: relative;
top: -2px; top: -2px;
...@@ -129,26 +93,18 @@ table a code { ...@@ -129,26 +93,18 @@ table a code {
border-bottom:1px solid #ccc; border-bottom:1px solid #ccc;
h4 { h4 {
color:#444; color:#666;
font-size:22px; font-size:18px;
line-height:38px;
padding-top:5px; padding-top:5px;
margin:2px; margin:2px;
font-weight:normal;
} }
} }
.git_url_wrapper { .git_url_wrapper {
margin-right:50px margin-right:50px
} }
.file_stats {
span {
img {
width:14px;
float:left;
margin-right:6px;
padding:2px 0;
}
}
}
.handle:hover { .handle:hover {
cursor:move; cursor:move;
...@@ -172,10 +128,6 @@ span.update-author { ...@@ -172,10 +128,6 @@ span.update-author {
display:block; display:block;
} }
/** END UPDATE ITEM **/ /** END UPDATE ITEM **/
.ajax-tab-loading {
padding:40px;
display:none;
}
.dashboard-loader { .dashboard-loader {
float:left; float:left;
margin:10px; margin:10px;
...@@ -186,15 +138,144 @@ span.update-author { ...@@ -186,15 +138,144 @@ span.update-author {
font-weight:bold; font-weight:bold;
} }
a.project-update.titled { .neib {
position:relative; margin-right:10px;
padding-left:35% !important; }
.title-block {
padding:10px; .label {
width:35%; background-color: #474D57;
position:absolute;
left:0; &.label-tag {
top:0; background: none;
border: none;
padding:4px 6px;
color:#444;
text-shadow:0 0 1px #fff;
&.grouped {
float: left;
margin-right: 6px;
padding: 6px;
}
}
&.label-issue {
background-color: #eee;
border: 1px solid #ccc;
padding:4px 6px;
color:#444;
text-shadow:0 0 1px #fff;
&.grouped {
float: left;
margin-right: 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 {
@extend .label;
background-color: #999;
&.pushed {
background-color: #4A97BD;
}
&.opened {
background-color: #469847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
&.joined {
background-color: #1ca9dd;
}
&.left {
background-color: #888;
float:none;
}
}
form {
@extend .form-horizontal;
.actions {
@extend .form-actions;
}
.clearfix {
@extend .control-group;
}
.input {
@extend .controls;
}
label {
@extend .control-label;
}
.xlarge {
@extend .input-xlarge;
}
.xxlarge {
@extend .input-xxlarge;
}
}
.field_with_errors {
display:inline;
}
ul.breadcrumb {
background:white;
border:none;
li {
display: inline;
text-shadow: 0 1px 0 white
}
a {
color:#474D57;
font-weight:bold;
font-size:14px;
}
.arrow {
background: url("images.png") no-repeat -85px -77px;
width: 19px;
height: 16px;
float: left;
position: relative;
left: -10px;
padding:0;
margin:0;
}
}
input[type=text] {
&.large_text {
padding:6px;
font-size:16px;
} }
} }
...@@ -270,40 +351,6 @@ p.time { ...@@ -270,40 +351,6 @@ p.time {
} }
/**
* Dashboard page
*
*/
.dashboard_category {
margin-bottom:30px;
h3 a {
color:#474D57;
&:hover {
text-decoration:underline;
}
}
.dashboard_block {
.dash_project_item {
margin-bottom:10px;
border:none;
padding:0px 5px;
.project_link {
color:#888;
&:hover {
color:#111;
.ico.project {
background-position:-209px -21px;
}
}
}
h4 {
color:#666;
}
}
}
}
.styled_image { .styled_image {
border:2px solid #ddd; border:2px solid #ddd;
} }
...@@ -393,46 +440,48 @@ p.time { ...@@ -393,46 +440,48 @@ p.time {
} }
} }
.btn { .votes {
&.very_small { font-size: 13px;
font-size:11px; line-height: 15px;
padding:2px 6px; .progress {
margin:2px; height: 4px;
margin: 0;
.bar {
float: left;
height: 100%;
} }
.bar-success {
&.grouped { background-color: #468847;
margin-right:7px; @include bg-gradient(#62C462, #51A351);
float:left;
} }
.bar-danger {
&.padded { background-color: #B94A48;
margin-right:3px; @include bg-gradient(#EE5F5B, #BD362F);
padding:4px 10px 4px; }
}
.upvotes {
display: inline-block;
color: #468847;
}
.downvotes {
display: inline-block;
color: #B94A48;
} }
} }
.votes-block {
margin: 14px 6px 6px 0;
.prettyprint { .downvotes {
background-color: #fefbf3; float: right;
padding: 9px; }
border: 1px solid rgba(0,0,0,.2);
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.1);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.1);
box-shadow: 0 1px 2px rgba(0,0,0,.1);
}
.hint {
font-style: italic;
color: #999;
} }
.votes-inline {
.upvotes { display: inline-block;
font-size: 14px; margin: 0 8px;
font-weight: bold; .progress {
color: #468847; display: inline-block;
text-align: right; padding: 0 0 2px;
padding: 4px; width: 45px;
margin: 2px; }
} }
/* Fix for readme code (stopped it from being yellow) */ /* Fix for readme code (stopped it from being yellow) */
...@@ -549,14 +598,6 @@ li.note { ...@@ -549,14 +598,6 @@ li.note {
} }
/**
* Milestones list
*
*/
.milestone {
@extend .wll;
}
/** /**
* Admin area * Admin area
...@@ -603,11 +644,10 @@ li.note { ...@@ -603,11 +644,10 @@ li.note {
* *
*/ */
.event_lp { .event_lp {
@extend .alert-info; @extend .ui-box;
color:#777;
margin-bottom:20px; margin-bottom:20px;
padding:8px; padding:8px;
border-style: solid;
border-width: 1px;
@include border-radius(4px); @include border-radius(4px);
min-height:22px; min-height:22px;
...@@ -621,88 +661,19 @@ li.note { ...@@ -621,88 +661,19 @@ li.note {
cursor:pointer; cursor:pointer;
} }
/**
* Issues, MRs legend
*
*/
.list_legend {
float:left;
margin-right:20px;
.icon {
width:12px;
height:12px;
float:left;
margin-right:5px;
margin-top: 2px;
@include border-radius(4px);
&.today{
background: #ADA;
border:1px solid #8B8;
}
&.closed {
background: #DDD;
border:1px solid #BBB;
}
&.yours {
background: #AAD;
border:1px solid #88B;
}
&.merged {
background: #DAD;
border:1px solid #B8B;
}
}
.text {
padding-bottom: 10px;
float:left;
}
}
.merge_request, .merge_request,
.issue { .issue {
.list_legend {
margin-right: 5px;
margin-top: 14px;
.icon {
width:8px;
height:8px;
float:left;
margin-right:5px;
@include border-radius(4px);
border:1px solid #ddd;
}
}
&.today{ &.today{
background: #EFE; background: #EFE;
border-color:#CEC; border-color:#CEC;
.icon {
background: #ADA;
border:1px solid #8B8;
}
} }
&.closed { &.closed {
background: #F5f5f5; background: #F5f5f5;
border-color:#E5E5E5; border-color:#E5E5E5;
.icon {
background: #DDD;
border:1px solid #BBB;
}
}
&.yours {
.icon {
background: #AAD;
border:1px solid #88B;
}
} }
&.merged { &.merged {
background: #F5f5f5; background: #F5f5f5;
border-color:#E5E5E5; border-color:#E5E5E5;
.icon {
background: #DAD;
border:1px solid #B8B;
}
} }
} }
...@@ -714,7 +685,7 @@ li.note { ...@@ -714,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;
...@@ -743,3 +714,31 @@ li.note { ...@@ -743,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;
}
}
body {
margin-bottom:20px;
}
pre {
font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
&.dark {
background: #333;
color:#f5f5f5;
}
}
a {
outline: none;
color: $link_color;
&:hover {
text-decoration:none;
color: $blue_link;
}
&.btn {
color: $style_color;
}
&.dark {
color: $style_color;
}
&.lined {
text-decoration:underline;
&:hover { text-decoration:underline; }
}
&.gray {
color:gray;
}
&.supp_diff_link {
text-align:center;
padding:20px 0;
background:#f1f1f1;
width:100%;
float:left;
}
&.neib {
margin-right:15px;
}
}
.neib {
margin-right:10px;
}
.alert-message {
@extend .alert;
&.success {
@extend .alert-success;
}
&.error {
@extend .alert-error;
}
}
.alert {
&.alert-well {
background:#ddd;
border:1px solid #ccc;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #ddd), to(#dfdfdf));
background-image: -webkit-linear-gradient(#ddd 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#ddd 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#ddd 6.6%, #dfdfdf);
color:#111;
}
}
h3, h4, h5, h6 {
line-height: 36px;
}
h5 {
font-size:14px;
}
table {
width:100%;
th {
padding-top: 9px;
font-weight: bold;
vertical-align: middle;
}
th, td {
padding: 10px 10px 9px;
line-height: 18px;
text-align: left;
}
&.bordered-table {
border: 1px solid #DDD;
border-collapse: separate;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
&.zebra-striped {
@extend .table-striped;
}
}
.btn {
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f1f1f1), color-stop(25%, #f1f1f1), to(#e6e6e6));
background-image: -webkit-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
background-image: -moz-linear-gradient(top, #f1f1f1, #f1f1f1 25%, #e6e6e6);
background-image: -ms-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
background-image: -o-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
background-image: linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6);
&:hover {
}
&.btn-primary {
background:$link_color;
border-color: #2A79A3;
&:hover {
background:$blue_link;
}
}
&.primary {
@extend .btn-primary;
}
&.success {
color: #fff;
text-shadow: 0 0 1px #111;
background: #5bb75b;;
font-weight: bold;
&:hover {
background-color: #51a351;
color: #fff;
}
}
&.danger,
&.btn-danger {
color:#fff;
background: #DA4E49;
border-color: #BD362F;
&:hover {
color:#fff;
background: #EE4E49;
}
}
&.danger {
@extend .btn-danger;
}
&.small {
@extend .btn-small;
}
&.active {
border-color:#aaa;
background-color:#ccc;
}
}
a:focus {
outline: none;
}
.nav-pills a:hover {
background-color:#888;
}
.nav-pills .active a {
background-color: $style_color;
}
.label {
background-color: #474D57;
&.label-important {
background-color: #B94A48;
}
&.label-issue {
background-color: #eee;
border: 1px solid #ccc;
padding:4px 6px;
color:#444;
text-shadow:0 0 1px #fff;
&.grouped {
float: left;
margin-right: 6px;
padding: 6px;
}
}
}
.nav-tabs > li > a, .nav-pills > li > a {
color:$style_color;
}
.nav-tabs > .active > a {
font-weight:bold;
}
/** COLORS **/
.cgray { color:gray; }
.cred { color:#D12F19; }
.cgreen { color:#44aa22; }
.cblack { color:#111; }
.cdark { color:#444 }
.cwhite { color:#fff !important }
.bgred { background: #F2DEDE !important}
/** COMMON STYLES **/
.left {
float:left;
}
.right {
float:right !important;
}
.width-50p{
width:50%;
}
.width-49p{
width:49%;
}
.width-30p{
width:30%;
}
.width-65p{
width:65%;
}
.width-100p{
width:100%;
}
.append-bottom-10 {
margin-bottom:10px;
}
.append-bottom-20 {
margin-bottom:20px;
}
.prepend-top-10 {
margin-top:10px;
}
.prepend-top-20 {
margin-top:20px;
}
.padded {
padding:20px;
}
.ipadded {
padding:20px !important;
}
.lborder {
border-left:1px solid #eee;
}
.borders {
border: 1px solid #ccc;
@include shade;
}
.no-borders {
border:none;
}
table.no-borders {
border:none;
tr, td { border:none }
}
.no-padding {
padding:0 !important;
}
.underlined {
border-bottom: 1px solid $border_color;
}
.vlink {
color: $link_color !important;
}
.pretty_label {
@include round-borders-all(4px);
padding:2px 4px;
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
color: #777;
border: 1px solid #DEDFE1;
&.branch {
border:none;
font-size:13px;
background: #474D57;
color:#fff;
font-weight:bold;
font-family: monospace;
}
}
.event_label {
@extend .label;
background-color: #999;
&.pushed {
background-color: #3A87AD;
}
&.opened {
background-color: #468847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
}
img.avatar {
float:left;
margin-right:15px;
width:40px;
border:1px solid #ddd;
padding:1px;
&.s16 {
width:16px;
height:16px;
}
&.s24 {
width:24px;
height:24px;
}
&.s32 {
width:32px;
height:32px;
}
}
img.lil_av {
padding-left: 4px;
padding-right:3px;
}
form {
@extend .form-horizontal;
.actions {
@extend .form-actions;
}
.clearfix {
@extend .control-group;
}
.input {
@extend .controls;
}
label {
@extend .control-label;
}
.xlarge {
@extend .input-xlarge;
}
.xxlarge {
@extend .input-xxlarge;
}
}
/**
* List li block element #1
*
*/
.wll {
background-color: #FFF;
padding: 10px 5px;
min-height: 20px;
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&.smoke {
background-color:#f5f5f5;
}
&:hover {
background:$hover;
}
&:last-child { border:none }
p { padding-top:5px; margin:0; color:$style_color;}
.author { color: #999; }
p {
color:#222;
margin-bottom: 0;
img {
position:relative;
top:3px;
}
}
}
/**
* Block element #2
*
*/
.entry {
position: relative;
padding: 7px 15px;
margin-bottom: 18px;
color: #404040;
filter:none;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
background:#F1F1F1;
border: 1px solid #ccc;
p {
color:$style_color;
margin-bottom: 0;
img {
position:relative;
top:3px;
}
}
}
/**
* Big UI Block for show page content
*
*/
.ui-box {
background:#F9F9F9;
margin-bottom: 25px;
@include round-borders-all(4px);
border-color: #CCC;
@include solid_shade;
ul {
margin:0;
}
h5, .title {
padding: 0 10px;
@include round-borders-top(4px);
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
&.small {
line-height: 28px;
font-size: 14px;
line-height:28px;
text-shadow: 0 1px 1px white;
}
form {
padding:9px 0;
margin:0px;
}
.nav-pills {
li {
padding:3px 0;
&.active a { background-color:$style_color; }
a {
border-radius:7px;
}
}
}
}
.bottom {
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
@include round-borders-bottom(4px);
border-bottom:none;
border-top: 1px solid #bbb;
}
&.padded {
h5, .title {
margin: -20px;
margin-bottom: 0;
padding: 5px 20px;
}
.middle_title {
background:#f5f5f5;
margin:20px -20px;
padding: 0 20px;
border-top:1px solid #eee;
border-bottom:1px solid #eee;
font-size:14px;
color:#777;
}
}
.row_title {
font-weight:bold;
color:#444;
&:hover {
color:#444;
text-decoration:underline;
}
}
li, .wll {
padding:10px;
&:first-child {
@include round-borders-top(4px);
border-top:none;
}
&:last-child {
@include round-borders-bottom(4px);
border:none;
}
}
}
table.admin-table {
@extend .table-bordered;
@extend .zebra-striped;
@include solid_shade;
th {
border-color: #CCC;
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
}
.field_with_errors {
display:inline;
}
ul.breadcrumb {
background:white;
border:none;
li {
display: inline;
text-shadow: 0 1px 0 white
}
a {
color:#474D57;
font-weight:bold;
font-size:14px;
}
.arrow {
background: url("images.png") no-repeat -85px -77px;
width: 19px;
height: 16px;
float: left;
position: relative;
left: -10px;
padding:0;
margin:0;
}
}
.nothing_here_message {
text-align:center;
padding:20px;
color:#777;
}
/**
* UI box element
* contains top, middle, bottom blocks
*
*/
.main_box {
@extend .borders;
@extend .prepend-top-20;
@extend .append-bottom-20;
border-width:1px;
@include solid_shade;
img { max-width: 100%; }
pre {
code {
background: none !important;
}
}
.top_box_content,
.middle_box_content,
.bottom_box_content {
padding:15px;
pre {
background: none !important;
margin:0;
border:none;
padding:0;
}
}
.middle_box_content {
border-radius:0;
border:none;
font-size:12px;
background-color:#f5f5f5;
border:none;
border-top:1px solid #eee;
}
.bottom_box_content {
border-top:1px solid #eee;
}
}
input[type=text] {
&.large_text {
padding:6px;
font-size:16px;
}
}
p {
&.slead {
color:#456;
font-size:16px;
margin-bottom: 12px;
font-weight: 200;
line-height: 24px;
}
}
h3.page_title {
color:#456;
font-size:20px;
font-weight: normal;
line-height: 28px;
}
/**
* File content holder
*
*/
.file_holder {
border:1px solid #CCC;
margin-bottom:1em;
@include solid_shade;
.file_title {
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
padding: 9px 10px;
height:18px;
.options {
float:right;
margin-top: -5px;
}
.file_name {
color:$style_color;
font-size:14px;
text-shadow: 0 1px 1px #fff;
small {
color:#999;
font-size:13px;
}
}
}
.file_content {
background:#fff;
font-size: 11px;
&.wiki {
font-size: 13px;
code {
padding:0 4px;
}
padding:20px;
h1, h2 {
line-height: 46px;
}
h3, h4 {
line-height: 40px;
}
}
&.image_file {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
&.blob_file {
}
/**
* Blame file
*/
&.blame {
tr {
border-bottom: 1px solid #eee;
}
td {
padding:5px;
}
.author,
.blame_commit {
background:#f5f5f5;
vertical-align:top;
}
.lines {
pre {
padding:0;
margin:0;
background:none;
border:none;
}
}
}
&.logs {
background:#eee;
max-height: 700px;
overflow-y: auto;
ol {
margin-left:40px;
padding: 10px 0;
border-left: 1px solid #CCC;
margin-bottom:0;
background: white;
li {
color:#888;
p {
margin:0;
color:#333;
line-height:24px;
padding-left: 10px;
}
&:hover {
background:$hover;
}
}
}
}
/**
* Code file
*/
&.code {
padding:0;
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#666;
}
}
}
}
/**
* ===================================
* Contain 3 main UI block elements:
* .main_box - for show pages
* .ui-box - for simple block & widgets
* ===================================
*/
/**
* UI box element
* contains top, middle, bottom blocks
*
*/
.main_box {
@extend .borders;
@extend .prepend-top-20;
@extend .append-bottom-20;
border-width:1px;
@include solid_shade;
img { max-width: 100%; }
pre {
code {
background: none !important;
}
}
.top_box_content,
.middle_box_content,
.bottom_box_content {
padding:15px;
pre {
background: none !important;
margin:0;
border:none;
padding:0;
}
}
.middle_box_content {
border-radius:0;
border:none;
font-size:12px;
background-color:#f5f5f5;
border:none;
border-top:1px solid #eee;
}
.bottom_box_content {
border-top:1px solid #eee;
}
}
/**
* Big UI Block for show page content
*
*/
.ui-box {
background:#F9F9F9;
margin-bottom: 25px;
@include round-borders-all(4px);
border-color: #CCC;
@include solid_shade;
&.white {
background:#fff;
}
ul {
margin:0;
}
h5, .title {
padding: 0 10px;
@include round-borders-top(4px);
@include bg-gray-gradient;
border-bottom: 1px solid #bbb;
&.small {
line-height: 28px;
font-size: 14px;
line-height:28px;
text-shadow: 0 1px 1px white;
}
form {
padding:9px 0;
margin:0px;
}
.nav-pills {
li {
padding:3px 0;
&.active a { background-color:$style_color; }
a {
border-radius:7px;
}
}
}
}
.bottom {
@include bg-gray-gradient;
@include round-borders-bottom(4px);
border-bottom:none;
border-top: 1px solid #bbb;
}
&.padded {
h5, .title {
margin: -20px;
margin-bottom: 0;
padding: 5px 20px;
}
.middle_title {
background:#f5f5f5;
margin:20px -20px;
padding: 0 20px;
border-top:1px solid #eee;
border-bottom:1px solid #eee;
font-size:14px;
color:#777;
}
}
.row_title {
font-weight:bold;
color:#444;
&:hover {
color:#444;
text-decoration:underline;
}
}
li, .wll {
padding:10px;
&:first-child {
@include round-borders-top(4px);
border-top:none;
}
&:last-child {
@include round-borders-bottom(4px);
border:none;
}
}
.ui-box-body {
padding:10px;
}
}
.btn {
@include bg-gradient(#f7f7f7, #d5d5d5);
border-color:#aaa;
&:hover {
@include bg-gray-gradient;
border-color:#bbb;
color:#333;
}
&.primary {
background:#2a79A3;
@include bg-gradient(#47A7b7, #2585b5);
border-color: #2A79A3;
color:#fff;
text-shadow: 0 1px 1px #268;
&:hover {
background:$blue_link;
color:#fff;
}
&.disabled {
color:#fff;
background:#29B;
}
}
&.success {
@extend .btn-success;
&:hover {
@extend .btn-success;
background: #51a351;
}
&.disabled {
color:#fff;
background:#2b2;
}
}
&.save-btn {
@extend .wide;
@extend .primary;
}
&.cancel-btn {
float:right;
}
&.wide {
padding-left:30px;
padding-right:30px;
}
&.danger {
@extend .btn-danger;
border-color: #BD362F;
&:hover {
color:#fff;
background: #EE4E49;
}
}
&.danger {
@extend .btn-danger;
}
&.small {
@extend .btn-small;
}
&.active {
border-color:#aaa;
background-color:#ccc;
}
&.very_small {
font-size:11px;
padding:2px 6px;
margin:2px;
}
&.grouped {
margin-right:7px;
float:left;
}
&.padded {
margin-right:3px;
padding:4px 10px 4px;
}
}
/** COLORS **/
.cgray { color:gray }
.cred { color:#D12F19 }
.cgreen { color:#4a2 }
.cblack { color:#111 }
.cdark { color:#444 }
.cwhite { color:#fff!important }
.bgred { background:#F2DEDE!important }
/** COMMON CLASSES **/
.left { float:left }
.right { float:right!important }
.width-50p { width:50% }
.width-49p { width:49% }
.width-30p { width:30% }
.width-65p { width:65% }
.width-100p { width:100% }
.append-bottom-10 { margin-bottom:10px }
.append-bottom-20 { margin-bottom:20px }
.prepend-top-10 { margin-top:10px }
.prepend-top-20 { margin-top:20px }
.padded { padding:20px }
.ipadded { padding:20px!important }
.lborder { border-left:1px solid #eee }
.no-padding { padding:0 !important; }
.underlined { border-bottom: 1px solid #CCC; }
.no-borders { border:none; }
.vlink { color: $link_color !important; }
.borders { border: 1px solid #ccc; @include shade; }
.hint { font-style: italic; color: #999; }
/** PILLS & TABS**/
.nav-pills a:hover { background-color:#888; }
.nav-pills .active a { background-color: $style_color; }
.nav-tabs > li > a, .nav-pills > li > a { color:$style_color; }
.nav.nav-tabs {
li {
> a {
padding:8px 20px;
margin-right: 7px;
border-color: #EEE;
color:#888;
border-bottom: 1px solid #ddd;
.badge {
background-color: #eee;
color:#888;
text-shadow:0 1px 1px #fff;
}
}
&.active {
> a {
border-color: #CCC;
border-bottom: 1px solid #fff;
color:#333;
}
}
}
}
/** ALERT MESSAGES **/
.alert-message { @extend .alert; }
.alert-messag.success { @extend .alert-success; }
.alert-message.error { @extend .alert-error; }
/** AVATARS **/
img.avatar { float:left; margin-right:15px; width:40px; border:1px solid #ddd; padding:1px; }
img.avatar.s16 { width:16px; height:16px; }
img.avatar.s24 { width:24px; height:24px; }
img.avatar.s32 { width:32px; height:32px; }
img.lil_av { padding-left: 4px; padding-right:3px; }
/** HELPERS **/
.nothing_here_message { text-align:center; padding:20px; color:#777; }
p.slead { color:#456; font-size:16px; margin-bottom: 12px; font-weight: 200; line-height: 24px; }
/** FORMS **/
input[type='search'].search-text-input {
background-image: url("icon-search.png");
background-repeat: no-repeat;
background-position: 10px;
padding-left:25px;
@include border-radius(4px);
border:1px solid #ccc;
}
/**
* File content holder
*
*/
.file_holder {
border:1px solid #BBB;
margin-bottom:1em;
@include solid_shade;
.file_title {
border-bottom: 1px solid #bbb;
@include bg-dark-gray-gradient;
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
padding: 9px 10px;
height:18px;
.options {
float:right;
margin-top: -5px;
}
.file_name {
color:$style_color;
font-size:14px;
text-shadow: 0 1px 1px #fff;
small {
color:#999;
font-size:13px;
}
}
}
.file_content {
background:#fff;
font-size: 11px;
&.wiki {
font-size: 13px;
code {
padding:0 4px;
}
padding:20px;
h1, h2 {
line-height: 46px;
}
h3, h4 {
line-height: 40px;
}
}
&.image_file {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
&.blob_file {
}
/**
* Blame file
*/
&.blame {
tr {
border-bottom: 1px solid #eee;
}
td {
padding:5px;
}
.author,
.blame_commit {
background:#f5f5f5;
vertical-align:top;
}
.lines {
pre {
padding:0;
margin:0;
background:none;
border:none;
}
}
}
&.logs {
background:#eee;
max-height: 700px;
overflow-y: auto;
ol {
margin-left:40px;
padding: 10px 0;
border-left: 1px solid #CCC;
margin-bottom:0;
background: white;
li {
color:#888;
p {
margin:0;
color:#333;
line-height:24px;
padding-left: 10px;
}
&:hover {
background:$hover;
}
}
}
}
/**
* Code file
*/
&.code {
padding:0;
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#666;
}
}
}
}
/** LISTS **/
ul {
/**
* List li block element #1
*
*/
.wll {
background-color: #FFF;
padding: 10px 5px;
min-height: 20px;
border-bottom: 1px solid #eee;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&.smoke { background-color:#f5f5f5; }
&:hover { background:$hover; }
&:last-child { border:none }
.author { color: #999; }
p {
padding-top:5px;
margin:0;
color:#222;
img {
position:relative;
top:3px;
}
}
}
}
table {
@extend .table;
@extend .table-striped;
@include solid_shade;
border:1px solid #bbb;
width:100%;
th {
font-weight: bold;
vertical-align: middle;
border-bottom: 1px solid #bbb;
text-shadow: 0 1px 1px #fff;
@include bg-dark-gray-gradient;
}
th, td {
padding: 8px;
line-height: 18px;
text-align: left;
}
td {
border-color:#f1f1f1;
&:first-child {
border-left:1px solid #bbb;
}
&:last-child {
border-right:1px solid #bbb;
}
}
&.bordered {
@extend .table-bordered;
}
&.lite {
border:none;
box-shadow:none;
tr, td {
border:none;
background:none !important;
}
}
}
/**
* Headers
*
*/
h3, h4, h5, h6 { line-height: 36px; }
h5 { font-size:14px; }
h3.page_title {
color:#456;
font-size:20px;
font-weight: normal;
line-height: 28px;
}
/** CODE **/
pre {
font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
&.dark {
background: #333;
color:#f5f5f5;
}
}
/**
* Links
*
*/
a {
outline: none;
color: $link_color;
&:hover {
text-decoration:none;
color: $blue_link;
}
&.btn {
color: $style_color;
&:hover {
color: $style_color;
}
}
&.dark {
color: $style_color;
}
&.lined {
text-decoration:underline;
&:hover { text-decoration:underline; }
}
&.gray {
color:gray;
}
&.supp_diff_link {
text-align:center;
padding:20px 0;
background:#f1f1f1;
width:100%;
float:left;
}
&.neib {
margin-right:15px;
}
}
a:focus {
outline: none;
}
table.highlighttable table.highlighttable {
{
margin:0px; margin:0px;
padding:0px; padding:0px;
font-size:12px; font-size:12px;
table-layout:fixed; table-layout:fixed;
background: #EEE; background: #EEE;
box-shadow: none;
border: none;
td.linenos {
background:#eee;
border-left:none;
}
td.code {
border-right:none;
}
} }
td.code, td.code,
td.linenos{ td.linenos{
padding:0; padding:0;
......
@import "bootstrap"; @import "bootstrap";
@import "bootstrap-responsive"; @import "bootstrap-responsive";
/** GITLAB colors **/ /** GitLab colors **/
$text_color:#222; $link_color:#3A89A3;
$lite_text_color: #666;
$link_color:#2A79A3;
$active_link_color:#2FA0BB;
$active_bg_color:#79C3E0;
$active_bd_color: #2FA0BB;
$border_color:#CCC;
$lite_border_color:#EEE;
$min_app_width:980px;
$max_app_width:980px;
$app_padding:20px;
$bg_color: #FFF;
$styled_border_color: #2FA0BB;
$color: "#4BB8D2";
$blue_link: #2fa0bb; $blue_link: #2fa0bb;
$style_color: #474d57;
$hover: #fdf5d9;
/** GitLab Fonts **/
/** Style colors **/
$style_color: #474D57;
$hover: #FDF5D9;
/** GITLAB Fonts **/
@font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); } @font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); }
/** MIXINS **/ /** MIXINS **/
...@@ -72,7 +56,27 @@ $hover: #FDF5D9; ...@@ -72,7 +56,27 @@ $hover: #FDF5D9;
border-radius: $radius; border-radius: $radius;
} }
@mixin bg-gradient($from, $to) {
background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
background-image: -webkit-linear-gradient($from, $to);
background-image: -moz-linear-gradient($from, $to);
background-image: -o-linear-gradient($from, $to);
}
@mixin bg-gray-gradient {
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
@mixin bg-dark-gray-gradient {
background:#eee;
background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
}
/** /**
* Header of application. * Header of application.
...@@ -109,11 +113,17 @@ $hover: #FDF5D9; ...@@ -109,11 +113,17 @@ $hover: #FDF5D9;
@import "themes/ui_modern.scss"; @import "themes/ui_modern.scss";
/** /**
* Gitlab bootstrap. * GitLab bootstrap.
* Overrides some styles of twitter bootstrap. * Overrides some styles of twitter bootstrap.
* Also give some common classes for gitlab app * Also give some common classes for GitLab app
*/ */
@import "gitlab_bootstrap.scss"; @import "gitlab_bootstrap/common.scss";
@import "gitlab_bootstrap/typography.scss";
@import "gitlab_bootstrap/buttons.scss";
@import "gitlab_bootstrap/blocks.scss";
@import "gitlab_bootstrap/files.scss";
@import "gitlab_bootstrap/tables.scss";
@import "gitlab_bootstrap/lists.scss";
/** /**
...@@ -125,7 +135,6 @@ $hover: #FDF5D9; ...@@ -125,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
*/ */
...@@ -151,6 +160,11 @@ $hover: #FDF5D9; ...@@ -151,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
*/ */
......
.git_url_wrapper { margin-right:50px }
.sidebar aside a{
display: block;
position: relative;
padding: 15px 10px;
margin: 10px 0 0 0;
font-size:13px;
font-weight:bold;
color:#333;
&.current {
color: white;
background: $active_bg_color;
border: 1px solid $active_bd_color;
border-radius:5px;
-webkit-border-top-right-radius: 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-topright: 0px;
-moz-border-radius-bottomright: 0px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin-right: -1px;
}
}
body table .commit a{color: #{$blue_link}}
body table th, body table td{ border-bottom: 1px solid #DEE2E3;}
body .fixed{position: fixed; }
/** File stat **/
.file_stats {
span {
img {
width:14px;
float:left;
margin-right: 6px;
padding:2px 0;
}
}
}
.round-borders {
@include round-borders-all(4px);
padding: 4px 0px;
}
table.round-borders {
float:left;
text-align: left;
}
/** PROJECTS **/
input.ssh_project_url {
padding:5px;
margin:0px;
float:right;
width:400px;
text-align:center;
}
#projects-list .project {
height:50px;
}
#tree-slider .tree-item,
#projects-list .project,
#snippets-table .snippet,
#issues-table .issue{
cursor:pointer;
}
.clear {
clear: both;
}
#user_projects_limit{
width: 60px;
}
.handle:hover{
cursor: move;
}
.project-refs-form {
span {
background: none !important;
position:static !important;
width:auto !important;
height: auto !important;
}
}
.project-refs-select {
width:200px;
}
.filter .left { margin-right:15px; }
body table .commit {
a.tree-commit-link {
color:#444;
&:hover {
text-decoration:underline;
}
}
}
/** NEW PROJECT **/
.new-project-hodler {
.icon span { background-position: -31px -70px; }
td { border-bottom: 1px solid #DEE2E3; }
}
/** Feed entry **/
.commit,
.snippet,
.message {
.title {
color:#666;
a { color:#666 !important; }
p { margin-top:0px; }
}
.author { color: #999 }
}
/** JQuery UI **/
.ui-autocomplete { @include round-borders-all(5px); }
.ui-menu-item { cursor: pointer }
.ui-selectmenu{
@include round-borders-all(4px);
margin-right:10px;
font-size:1.5em;
height:auto;
font-weight:bold;
.ui-selectmenu-status {
padding:3px 10px;
}
}
#holder {
background:#FAFAFA;
border: 1px solid #EEE;
cursor: move;
height: 70%;
overflow: hidden;
}
/* Project Dashboard Page */
html, body { height: 100%; }
.news-feed h2{float: left;}
.news-feed .project-updates {margin-bottom: 20px; display: block; width: 100%;}
.news-feed .project-updates .data{ padding: 0}
.news-feed .project-updates a.project-update {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
.news-feed .project-updates a.project-update:last-child{border-bottom: 0}
.news-feed .project-updates a.project-update img{float: left; margin-right: 10px;}
.news-feed .project-updates a.project-update span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;}
.news-feed .project-updates a.project-update span.update-title{margin-bottom: 10px}
.news-feed .project-updates a.project-update span.update-author{color: #999; font-weight: normal; font-style: italic;}
.news-feed .project-updates a.project-update span.update-author strong{font-weight: bold; font-style: normal;}
/* eo Dashboard Page */
/** Update entry **/
.update-data { padding: 0 }
.update-data { width:100%; }
.update-data.ui-box .data { padding:0; }
a.update-item {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
a.update-item:last-child{border-bottom: 0}
a.update-item img{float: left; margin-right: 10px;}
a.update-item span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;}
a.update-item span.update-title{margin-bottom: 10px}
a.update-item span.update-author{color: #999; font-weight: normal; font-style: italic;}
a.update-item span.update-author strong{font-weight: bold; font-style: normal;}
body .team_member_new .span-6, .team_member_edit .span-6{ padding:10px 0; }
body.projects-page input.text.git-url.project_list_url { width:165px; }
body table.no-borders th {
background:none;
border-bottom:1px solid #CCC;
color:#333;
}
body table.no-borders tr,
body table.no-borders td{
border:none;
}
.ajax-tab-loading {
padding:40px;
display:none;
}
#tree-content-holder { float:left; width:100%; }
#tree-readme-holder {
float:left;
width:100%;
.readme {
@include round-borders-all(4px);
padding: 4px 15px;
background:#F7F7F7;
}
}
/* Commit Page */
.entity-info {float: right;}
.entity-button{
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.192, #fff), to(#f4f4f4));
background-image: -webkit-linear-gradient(#fff 19.2%, #f4f4f4);
background-image: -moz-linear-gradient(#fff 19.2%, #f4f4f4);
background-image: -o-linear-gradient(#fff 19.2%, #f4f4f4);
box-shadow: 0 -1px 0 white inset;
display: block;
border: 1px solid #eee;
border-radius: 5px;
margin-bottom: 2px;
position: relative;
padding: 4px 10px;
font-size: 11px;
padding-right: 20px;
}
.entity-button i{
background: url('images.png') no-repeat -138px -27px;
width: 6px;
height: 9px;
float: right;
position: absolute;
top: 6px;
right: 5px;
}
.box-arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999; margin: 1.5em 0;}
h4.dash-tabs {
margin: 0;
border-bottom: 1px solid #ccc;
padding: 10px 10px;
font-size: 11px;
padding-left:20px;
font-weight: bold; text-transform: uppercase;
background: #F7F7F7;
margin-bottom:20px;
height:13px;
}
.dash-button {
border-right: 1px solid #ddd;
background:none;
padding: 10px 15px;
float:left;
position:relative;
top:-10px;
left:0px;
height:13px;
&:first-child {
border-left: 1px solid #ddd;
}
&.active {
background: #eaeaea;
}
}
.dashboard-loader {
float:right;
margin-right:30px;
display:none;
}
.merge-tabs {
margin: 0;
border: 1px solid #ccc;
padding: 5px;
font-size: 12px;
background: #F7F7F7;
margin-bottom:20px;
height:26px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
.tab {
font-weight: bold;
border-right: 1px solid #ddd;
background:none;
padding: 10px;
min-width:60px;
float:left;
position:relative;
top:-5px;
left:-5px;
height:16px;
padding-left:34px;
span {
width: 20px;
height: 20px;
display: inline-block;
position: absolute;
left: 8px;
top: 8px;
}
&.active {
background: #eaeaea;
}
}
}
.merge-tabs.repository .tab span{ background: url("images.png") no-repeat -38px -77px; }
.activities-tab span { background: url("images.png") no-repeat -161px -1px; }
.stat-tab span,
.team-tab span,
.snippets-tab span { background: url("images.png") no-repeat -38px -77px; }
.files-tab span { background: url("images.png") no-repeat -112px -23px; }
.merge-notes-tab span { background: url("images.png") no-repeat -161px -1px; }
.merge-commits-tab span { background: url("images.png") no-repeat -86px 1px; }
.merge-diffs-tab span { background: url("images.png") no-repeat -118px 1px; }
.merge-tabs .dashboard-loader { padding:8px; }
.user-mention {
color: #2FA0BB;
font-weight: bold;
}
.author {
color: #999;
}
.dark_scheme_box {
padding:20px 0;
label {
float:left;
box-shadow: 0 0px 5px rgba(0,0,0,.3);
img {
}
}
}
a.project-update.titled {
position: relative;
padding-left: 235px !important;
.title-block {
padding: 10px;
width: 205px;
position: absolute;
left: 0;
top: 0;
}
}
.add_new {
float: right;
background: #A6B807;
color: white;
padding: 4px 10px;
@include round-borders-all(4px);
font-size:11px;
margin: 10px 0;
}
...@@ -20,22 +20,30 @@ ...@@ -20,22 +20,30 @@
.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;
} }
} }
.chzn-single { .chzn-single {
background:#ddd; @include bg-gray-gradient;
//border:none;
//box-shadow:none;
div { div {
background:transparent; background:transparent;
......
...@@ -206,4 +206,24 @@ ...@@ -206,4 +206,24 @@
min-width:65px; min-width:65px;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace; font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
} }
.commit-author-name {
color: #777;
}
}
.diff_file_header a,
.file_stats a {
color:$style_color;
}
.file_stats {
span {
img {
width:14px;
float:left;
margin-right:6px;
padding:2px 0;
}
}
} }
...@@ -6,11 +6,7 @@ ...@@ -6,11 +6,7 @@
h4 { h4 {
padding:0 10px; padding:0 10px;
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
background:#eee; @include bg-gray-gradient;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
} }
.graph { .graph {
......
...@@ -22,7 +22,7 @@ header { ...@@ -22,7 +22,7 @@ header {
* *
*/ */
.app_logo { .app_logo {
width:230px; width:200px;
float:left; float:left;
position:relative; position:relative;
top:-5px; top:-5px;
...@@ -31,7 +31,7 @@ header { ...@@ -31,7 +31,7 @@ header {
h1 { h1 {
padding-top: 5px; padding-top: 5px;
width:102px; width:90px;
background: url('logo_dark.png') no-repeat 0px -3px; background: url('logo_dark.png') no-repeat 0px -3px;
float:left; float:left;
margin-left:5px; margin-left:5px;
......
...@@ -65,6 +65,11 @@ input.check_all_issues { ...@@ -65,6 +65,11 @@ input.check_all_issues {
} }
} }
@media (min-width: 800px) { .issues_filters select { width:160px; } }
@media (min-width: 1000px) { .issues_filters select { width:200px; } }
@media (min-width: 1200px) { .issues_filters select { width:220px; } }
#issues-table-holder { #issues-table-holder {
.issues_filters { .issues_filters {
form { form {
...@@ -99,3 +104,12 @@ input.check_all_issues { ...@@ -99,3 +104,12 @@ input.check_all_issues {
#update_status { #update_status {
width:100px; width:100px;
} }
/**
* Milestones list
*
*/
.milestone {
@extend .wll;
}
...@@ -11,23 +11,6 @@ ...@@ -11,23 +11,6 @@
background:#f1f1f1; background:#f1f1f1;
} }
.commit {
margin:0;
padding:0;
padding: 5px;
margin-bottom: 5px;
.committed_ago {
display:none;
}
.browse_code_link_holder {
display:none;
}
list-style:none;
&:hover {
background:none;
}
}
} }
/** /**
...@@ -55,6 +38,7 @@ ...@@ -55,6 +38,7 @@
background: #CEB; background: #CEB;
.accept_merge_request { .accept_merge_request {
font-size:13px;
float:left; float:left;
} }
.remove_branch_holder { .remove_branch_holder {
...@@ -99,3 +83,41 @@ li.merge_request { ...@@ -99,3 +83,41 @@ li.merge_request {
@extend .padded; @extend .padded;
@extend .append-bottom-10; @extend .append-bottom-10;
} }
.label_branch {
@include round-borders-all(4px);
padding:2px 4px;
border:none;
font-size:14px;
background: #474D57;
color:#fff;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
}
.mr_source_commit,
.mr_target_commit {
.commit {
margin:0;
padding:0;
padding: 5px;
margin-bottom: 5px;
.avatar { position:relative }
.row_title {
color:#444;
}
.commit-author-name,
.dash,
.committed_ago,
.browse_code_link_holder {
display:none;
}
list-style:none;
&:hover {
background:none;
}
}
}
.mr_direction_tip {
margin-top:40px
}
...@@ -6,13 +6,9 @@ ul.main_menu { ...@@ -6,13 +6,9 @@ ul.main_menu {
border-radius: 4px; border-radius: 4px;
margin: auto; margin: auto;
margin:30px 0; margin:30px 0;
background:#eee; border:1px solid #AAA;
border:1px solid #bbb;
height:37px; height:37px;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); @include bg-gray-gradient;
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
position:relative; position:relative;
overflow:hidden; overflow:hidden;
@include shade; @include shade;
...@@ -59,7 +55,6 @@ ul.main_menu { ...@@ -59,7 +55,6 @@ ul.main_menu {
&.current { &.current {
background-color:#D5D5D5; background-color:#D5D5D5;
border-bottom: 2px solid $style_color;
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;
...@@ -89,7 +84,7 @@ ul.main_menu { ...@@ -89,7 +84,7 @@ ul.main_menu {
line-height:36px; line-height:36px;
color: $style_color; color: $style_color;
text-shadow:0 1px 1px white; text-shadow:0 1px 1px white;
padding:0 10px;
} }
} }
/* /*
......
...@@ -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,17 +26,21 @@ ...@@ -30,17 +26,21 @@
} }
#new_note { #new_note {
#note_note {
height:25px;
}
.attach_holder { .attach_holder {
display:none; display:none;
} }
} }
.preview_note {
margin: 2px;
border: 1px solid #ddd;
padding: 10px;
min-height: 60px;
background:#f5f5f5;
}
.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;}
...@@ -62,6 +62,23 @@ ...@@ -62,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{
...@@ -204,3 +221,8 @@ td .line_note_link { ...@@ -204,3 +221,8 @@ td .line_note_link {
} }
} }
} }
.note-text {
border: 1px solid #aaa;
box-shadow:none;
}
.profile_history {
.event_feed {
min-height:20px;
.avatar {
width:20px;
}
}
}
...@@ -14,6 +14,32 @@ ...@@ -14,6 +14,32 @@
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
padding: 2px 10px; padding: 2px 10px;
} }
ul {
li {
padding:0;
a {
display:block;
.project_name {
color:#4fa2bd;
font-size:14px;
line-height:18px;
}
.arrow {
float:right;
padding:10px;
margin:0;
}
.last_activity {
padding-top:5px;
display:block;
span, strong {
font-size:12px;
color:#666;
}
}
}
}
}
@extend .leftbar; @extend .leftbar;
@extend .ui-box; @extend .ui-box;
} }
...@@ -33,9 +59,38 @@ ...@@ -33,9 +59,38 @@
color:#888; color:#888;
} }
.btn { .btn {
padding:6px; padding:6px 10px;
margin-left:10px; margin-left:10px;
margin-bottom:8px; margin-bottom:8px;
} }
} }
.adv_settings {
h6 { margin-left:40px; }
}
}
.project_clone_panel {
@include border-radius(4px);
@include bg-gray-gradient;
padding: 4px 7px;
border: 1px solid #CCC;
margin-bottom:5px;
}
.project_clone_holder {
input[type="text"] {
border: 1px solid #BBB;
box-shadow: none;
}
}
.save-project-loader {
img {
margin-top:50px;
margin-bottom:50px;
}
h3 {
@extend .page_title;
}
} }
...@@ -29,7 +29,11 @@ ...@@ -29,7 +29,11 @@
@include border-radius(0); @include border-radius(0);
.tree-item { .tree-item {
&:hover { &:hover {
td { background: $hover; } td {
background: $hover;
border-top:1px solid #FEA;
border-bottom:1px solid #FEA;
}
cursor:pointer; cursor:pointer;
} }
} }
...@@ -53,31 +57,9 @@ ...@@ -53,31 +57,9 @@
#tree-slider { #tree-slider {
@include solid_shade;
width:100%;
border-color:#ccc;
td { td {
padding:8px;
border-color:#f1f1f1;
background:#fafafa; background:#fafafa;
} }
tr:first-child td:first-child,
tr:first-child td:last-child {
border-radius:0;
}
th {
border-color: #CCC;
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
} }
.tree-commit-link { .tree-commit-link {
......
class MergeRequestsLoad < BaseContext class MergeRequestsLoad < BaseContext
def execute def execute
type = params[:f].to_i type = params[:f]
merge_requests = project.merge_requests merge_requests = project.merge_requests
merge_requests = case type merge_requests = case type
when 1 then merge_requests when 'all' then merge_requests
when 2 then merge_requests.closed when 'closed' then merge_requests.closed
when 3 then merge_requests.opened.assigned(current_user) when 'assigned-to-me' then merge_requests.opened.assigned(current_user)
else merge_requests.opened else merge_requests.opened
end.page(params[:page]).per(20) end.page(params[:page]).per(20)
......
...@@ -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
...@@ -11,11 +11,11 @@ class ApplicationController < ActionController::Base ...@@ -11,11 +11,11 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can? helper_method :abilities, :can?
rescue_from Gitlab::Gitolite::AccessDenied do |exception| rescue_from Gitlab::Gitolite::AccessDenied do |exception|
render "errors/gitolite", layout: "error" render "errors/gitolite", layout: "error", status: 500
end end
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
render "errors/encoding", layout: "error", status: 404 render "errors/encoding", layout: "error", status: 500
end end
rescue_from ActiveRecord::RecordNotFound do |exception| rescue_from ActiveRecord::RecordNotFound do |exception|
...@@ -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
...@@ -116,22 +112,12 @@ class ApplicationController < ActionController::Base ...@@ -116,22 +112,12 @@ class ApplicationController < ActionController::Base
end end
end end
def load_refs
if params[:ref].blank?
@branch = params[:branch].blank? ? nil : params[:branch]
@tag = params[:tag].blank? ? nil : params[:tag]
@ref = @branch || @tag || @project.try(:default_branch) || Repository.default_ref
else
@ref = params[:ref]
end
end
def render_404 def render_404
render file: File.join(Rails.root, "public", "404"), layout: false, status: "404" render file: File.join(Rails.root, "public", "404"), layout: false, status: "404"
end end
def require_non_empty_project def require_non_empty_project
redirect_to @project unless @project.repo_exists? && @project.has_commits? redirect_to @project if @project.empty_repo?
end end
def no_cache_headers def no_cache_headers
......
...@@ -52,6 +52,7 @@ class CommitsController < ApplicationController ...@@ -52,6 +52,7 @@ class CommitsController < ApplicationController
@commits = result[:commits] @commits = result[:commits]
@commit = result[:commit] @commit = result[:commit]
@diffs = result[:diffs] @diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = [] @line_notes = []
@commits = CommitDecorator.decorate(@commits) @commits = CommitDecorator.decorate(@commits)
...@@ -64,7 +65,14 @@ class CommitsController < ApplicationController ...@@ -64,7 +65,14 @@ class CommitsController < ApplicationController
@commit.to_patch, @commit.to_patch,
type: "text/plain", type: "text/plain",
disposition: 'attachment', disposition: 'attachment',
filename: (@commit.id.to_s + ".patch") filename: "#{@commit.id}.patch"
) )
end end
protected
def load_refs
@ref ||= params[:ref].presence || params[:branch].presence || params[:tag].presence
@ref ||= @ref || @project.try(:default_branch) || 'master'
end
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]
...@@ -37,7 +37,7 @@ class IssuesController < ApplicationController ...@@ -37,7 +37,7 @@ class IssuesController < ApplicationController
end end
def new def new
@issue = @project.issues.new @issue = @project.issues.new(params[:issue])
respond_with(@issue) respond_with(@issue)
end end
...@@ -60,7 +60,13 @@ class IssuesController < ApplicationController ...@@ -60,7 +60,13 @@ class IssuesController < ApplicationController
@issue.save @issue.save
respond_to do |format| respond_to do |format|
format.html { redirect_to project_issue_path(@project, @issue) } format.html do
if @issue.valid?
redirect_to project_issue_path(@project, @issue)
else
render :new
end
end
format.js format.js
end end
end end
...@@ -81,8 +87,6 @@ class IssuesController < ApplicationController ...@@ -81,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|
...@@ -162,10 +166,10 @@ class IssuesController < ApplicationController ...@@ -162,10 +166,10 @@ class IssuesController < ApplicationController
def issues_filter def issues_filter
{ {
all: "1", all: "all",
closed: "2", closed: "closed",
to_me: "3", to_me: "assigned-to-me",
open: "0" open: "open"
} }
end end
end end
class LabelsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any issue
before_filter :authorize_read_issue!
respond_to :js, :html
def index
@labels = @project.issues.tag_counts_on(:labels).order('count DESC')
end
protected
def module_enabled
return render_404 unless @project.issues_enabled
end
end
...@@ -103,10 +103,12 @@ class MergeRequestsController < ApplicationController ...@@ -103,10 +103,12 @@ class MergeRequestsController < ApplicationController
def branch_from def branch_from
@commit = project.commit(params[:ref]) @commit = project.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end end
def branch_to def branch_to
@commit = project.commit(params[:ref]) @commit = project.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end end
protected protected
......
...@@ -17,8 +17,8 @@ class MilestonesController < ApplicationController ...@@ -17,8 +17,8 @@ class MilestonesController < ApplicationController
respond_to :html respond_to :html
def index def index
@milestones = case params[:f].to_i @milestones = case params[:f]
when 1; @project.milestones when 'all'; @project.milestones
else @project.milestones.active else @project.milestones.active
end end
......
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
......
...@@ -50,7 +50,7 @@ class ProjectsController < ApplicationController ...@@ -50,7 +50,7 @@ class ProjectsController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
if @project.repo_exists? && @project.has_commits? unless @project.empty_repo?
@last_push = current_user.recent_push(@project.id) @last_push = current_user.recent_push(@project.id)
render :show render :show
else else
......
require 'github/markup'
class RefsController < ApplicationController class RefsController < ApplicationController
include Gitlab::Encode include Gitlab::Encode
before_filter :project before_filter :project
......
...@@ -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])
...@@ -17,13 +20,12 @@ class TeamMembersController < ApplicationController ...@@ -17,13 +20,12 @@ class TeamMembersController < ApplicationController
end end
def create def create
@team_member = UsersProject.new(params[:team_member]) @project.add_users_ids_to_team(
@team_member.project = project params[:user_ids],
if @team_member.save params[:project_access]
redirect_to team_project_path(@project) )
else
render "new" redirect_to project_team_index_path(@project)
end
end end
def update def update
...@@ -33,7 +35,7 @@ class TeamMembersController < ApplicationController ...@@ -33,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
...@@ -41,7 +43,7 @@ class TeamMembersController < ApplicationController ...@@ -41,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
......
class CommitDecorator < ApplicationDecorator class CommitDecorator < ApplicationDecorator
decorates :commit decorates :commit
# Returns a string describing the commit for use in a link title
#
# Example
#
# "Commit: Alex Denisov - Project git clone panel"
def link_title
"Commit: #{author_name} - #{title}"
end
# Returns the commits title. # Returns the commits title.
# #
# Usually, the commit title is the first line of the commit message. # Usually, the commit title is the first line of the commit message.
# 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 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)
safe_message[0..69] << "&hellip;".html_safe safe_message[0..69] << "&hellip;".html_safe
...@@ -26,4 +37,10 @@ class CommitDecorator < ApplicationDecorator ...@@ -26,4 +37,10 @@ class CommitDecorator < ApplicationDecorator
safe_message.split(/\n/, 2)[1].try(:chomp) safe_message.split(/\n/, 2)[1].try(:chomp)
end end
end end
protected
def no_commit_message
"--no commit message"
end
end end
...@@ -8,6 +8,8 @@ class EventDecorator < ApplicationDecorator ...@@ -8,6 +8,8 @@ class EventDecorator < ApplicationDecorator
"#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title "#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title
elsif self.push? elsif self.push?
"#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name "#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name
elsif self.membership_changed?
"#{self.author_name} #{self.action_name} #{self.project.name}"
else else
"" ""
end end
......
...@@ -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"
...@@ -126,4 +127,19 @@ module ApplicationHelper ...@@ -126,4 +127,19 @@ module ApplicationHelper
def hexdigest(string) def hexdigest(string)
Digest::SHA1.hexdigest string Digest::SHA1.hexdigest string
end end
def project_last_activity project
activity = project.last_activity
if activity && activity.created_at
time_ago_in_words(activity.created_at) + " ago"
else
"Never"
end
end
def authbutton(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag("authbuttons/#{file_name}",
alt: "Sign in with #{provider.to_s.titleize}")
end
end end
module GitlabMarkdownHelper module GitlabMarkdownHelper
# Replaces references (i.e. @abc, #123, !456, ...) in the text with links to include Gitlab::Markdown
# the appropriate items in Gitlab.
#
# text - the source text
# html_options - extra options for the reference links as given to link_to
#
# note: reference links will only be generated if @project is set
#
# see Gitlab::Markdown for details on the supported syntax
def gfm(text, html_options = {})
return text if text.nil?
return text if @project.nil?
# Extract pre blocks so they are not altered
# from http://github.github.com/github-flavored-markdown/
extractions = {}
text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
md5 = Digest::MD5.hexdigest(match)
extractions[md5] = match
"{gfm-extraction-#{md5}}"
end
# TODO: add popups with additional information
parser = Gitlab::Markdown.new(@project, html_options)
text = parser.parse(text)
# Insert pre block extractions
text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
extractions[$1]
end
text.html_safe
end
# Use this in places where you would normally use link_to(gfm(...), ...). # Use this in places where you would normally use link_to(gfm(...), ...).
# #
...@@ -44,7 +11,9 @@ module GitlabMarkdownHelper ...@@ -44,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
...@@ -54,7 +23,14 @@ module GitlabMarkdownHelper ...@@ -54,7 +23,14 @@ module GitlabMarkdownHelper
end end
def markdown(text) def markdown(text)
@__renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::GitlabHTML.new(self, filter_html: true, with_toc_data: true), { unless @markdown
gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self,
# see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch-
filter_html: true,
with_toc_data: true,
hard_wrap: true)
@markdown = Redcarpet::Markdown.new(gitlab_renderer,
# see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
no_intra_emphasis: true, no_intra_emphasis: true,
tables: true, tables: true,
fenced_code_blocks: true, fenced_code_blocks: true,
...@@ -62,9 +38,9 @@ module GitlabMarkdownHelper ...@@ -62,9 +38,9 @@ module GitlabMarkdownHelper
strikethrough: true, strikethrough: true,
lax_html_blocks: true, lax_html_blocks: true,
space_after_headers: true, space_after_headers: true,
superscript: true superscript: true)
}) end
@__renderer.render(text).html_safe @markdown.render(text).html_safe
end end
end end
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
module ProjectsHelper
def grouper_project_members(project)
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
end
def remove_from_team_message(project, member)
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
end
end
...@@ -8,7 +8,7 @@ module TabHelper ...@@ -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,10 +18,25 @@ module TreeHelper ...@@ -18,10 +18,25 @@ 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
content.name content.name
end end
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(.textile .rdoc .org .creole
.mediawiki .rst .asciidoc .pod))
end
def gitlab_markdown?(filename)
filename.end_with?(*%w(.mdown .md .markdown))
end
end end
...@@ -76,6 +76,21 @@ class Notify < ActionMailer::Base ...@@ -76,6 +76,21 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
end end
def project_access_granted_email(user_project_id)
@users_project = UsersProject.find user_project_id
@project = @users_project.project
mail(to: @users_project.user.email,
subject: subject("access to project was granted"))
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.id}", @issue.title))
end
private private
# Look up a User by their ID and return their email address # Look up a User by their ID and return their email address
...@@ -96,18 +111,18 @@ class Notify < ActionMailer::Base ...@@ -96,18 +111,18 @@ class Notify < ActionMailer::Base
# Examples # Examples
# #
# >> subject('Lorem ipsum') # >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum" # => "GitLab | Lorem ipsum"
# #
# # Automatically inserts Project name when @project is set # # Automatically inserts Project name when @project is set
# >> @project = Project.last # >> @project = Project.last
# => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...> # => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# >> subject('Lorem ipsum') # >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum | Ruby on Rails" # => "GitLab | Lorem ipsum | Ruby on Rails"
# #
# # Accepts multiple arguments # # Accepts multiple arguments
# >> subject('Lorem ipsum', 'Dolor sit amet') # >> subject('Lorem ipsum', 'Dolor sit amet')
# => "gitlab | Lorem ipsum | Dolor sit amet" # => "GitLab | Lorem ipsum | Dolor sit amet"
def subject(*extra) def subject(*extra)
"gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "") "GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
end end
end end
class Commit class Commit
include ActiveModel::Conversion include ActiveModel::Conversion
include Gitlab::Encode include Gitlab::Encode
include StaticModel
extend ActiveModel::Naming extend ActiveModel::Naming
attr_accessor :commit attr_accessor :commit
...@@ -22,7 +23,6 @@ class Commit ...@@ -22,7 +23,6 @@ class Commit
:to_patch, :to_patch,
to: :commit to: :commit
class << self class << self
def find_or_first(repo, commit_id = nil, root_ref) def find_or_first(repo, commit_id = nil, root_ref)
commit = if commit_id commit = if commit_id
...@@ -82,20 +82,24 @@ class Commit ...@@ -82,20 +82,24 @@ class Commit
end end
def compare(project, from, to) def compare(project, from, to)
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
result = { result = {
commits: [], commits: [],
diffs: [], diffs: [],
commit: nil commit: nil,
same: false
} }
return result unless from && to
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
if first && last if first && last
commits = [first, last].sort_by(&:created_at) commits = [first, last].sort_by(&:created_at)
younger = commits.first younger = commits.first
older = commits.last older = commits.last
result[:same] = (younger.id == older.id)
result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)} result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
result[:diffs] = project.repo.diff(younger.id, older.id) rescue [] result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older) result[:commit] = Commit.new(older)
...@@ -105,10 +109,6 @@ class Commit ...@@ -105,10 +109,6 @@ class Commit
end end
end end
def persisted?
false
end
def initialize(raw_commit, head = nil) def initialize(raw_commit, head = nil)
@commit = raw_commit @commit = raw_commit
@head = head @head = head
......
...@@ -10,6 +10,8 @@ class Event < ActiveRecord::Base ...@@ -10,6 +10,8 @@ class Event < ActiveRecord::Base
Pushed = 5 Pushed = 5
Commented = 6 Commented = 6
Merged = 7 Merged = 7
Joined = 8 # User joined project
Left = 9 # User left project
belongs_to :project belongs_to :project
belongs_to :target, polymorphic: true belongs_to :target, polymorphic: true
...@@ -37,7 +39,15 @@ class Event < ActiveRecord::Base ...@@ -37,7 +39,15 @@ class Event < ActiveRecord::Base
# - new issue # - new issue
# - merge request # - merge request
def allowed? def allowed?
push? || issue? || merge_request? push? || issue? || merge_request? || membership_changed?
end
def project_name
if project
project.name
else
"(deleted)"
end
end end
def push? def push?
...@@ -84,6 +94,18 @@ class Event < ActiveRecord::Base ...@@ -84,6 +94,18 @@ class Event < ActiveRecord::Base
[Closed, Reopened].include?(action) [Closed, Reopened].include?(action)
end end
def joined?
action == Joined
end
def left?
action == Left
end
def membership_changed?
joined? || left?
end
def issue def issue
target if target_type == "Issue" target if target_type == "Issue"
end end
...@@ -101,6 +123,10 @@ class Event < ActiveRecord::Base ...@@ -101,6 +123,10 @@ class Event < ActiveRecord::Base
"closed" "closed"
elsif merged? elsif merged?
"merged" "merged"
elsif joined?
'joined'
elsif left?
'left'
else else
"opened" "opened"
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
......
require 'digest/md5' require 'digest/md5'
class Key < ActiveRecord::Base class Key < ActiveRecord::Base
include SshKey
belongs_to :user belongs_to :user
belongs_to :project belongs_to :project
attr_protected :user_id
validates :title, validates :title,
presence: true, presence: true,
length: { within: 0..255 } length: { within: 0..255 }
validates :key, validates :key,
presence: true, presence: true,
format: { :with => /ssh-.{3} / },
length: { within: 0..5000 } length: { within: 0..5000 }
before_save :set_identifier before_save :set_identifier
...@@ -50,6 +52,10 @@ class Key < ActiveRecord::Base ...@@ -50,6 +52,10 @@ class Key < ActiveRecord::Base
user.projects user.projects
end end
end end
def last_deploy?
Key.where(identifier: identifier).count == 0
end
end end
# == Schema Information # == Schema Information
# #
......
...@@ -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"
...@@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base
end end
def automerge!(current_user) 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) self.merge!(current_user.id)
true true
end end
......
...@@ -28,18 +28,10 @@ class Milestone < ActiveRecord::Base ...@@ -28,18 +28,10 @@ class Milestone < ActiveRecord::Base
end end
def percent_complete def percent_complete
@percent_complete ||= begin ((self.issues.closed.count * 100) / self.issues.count).abs
total_i = self.issues.count rescue ZeroDivisionError
closed_i = self.issues.closed.count
if total_i > 0
(closed_i * 100) / total_i
else
100 100
end end
rescue => ex
0
end
end
def expires_at def expires_at
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
......
...@@ -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)
...@@ -103,7 +103,13 @@ class Note < ActiveRecord::Base ...@@ -103,7 +103,13 @@ class Note < ActiveRecord::Base
# Returns true if this is an upvote note, # Returns true if this is an upvote note,
# otherwise false is returned # otherwise false is returned
def upvote? def upvote?
note =~ /^\+1/ ? true : false note.start_with?('+1') || note.start_with?(':+1:')
end
# Returns true if this is a downvote note,
# otherwise false is returned
def downvote?
note.start_with?('-1') || note.start_with?(':-1:')
end end
end end
# == Schema Information # == Schema Information
......
...@@ -104,6 +104,8 @@ class Project < ActiveRecord::Base ...@@ -104,6 +104,8 @@ class Project < ActiveRecord::Base
length: { within: 1..255 } length: { within: 1..255 }
validates :owner, presence: true validates :owner, presence: true
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validate :check_limit validate :check_limit
validate :repo_name validate :repo_name
...@@ -158,7 +160,7 @@ class Project < ActiveRecord::Base ...@@ -158,7 +160,7 @@ class Project < ActiveRecord::Base
end end
def last_activity def last_activity
events.last || nil events.order("created_at ASC").last
end end
def last_activity_date def last_activity_date
...@@ -169,6 +171,10 @@ class Project < ActiveRecord::Base ...@@ -169,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
...@@ -187,7 +193,7 @@ end ...@@ -187,7 +193,7 @@ end
# private_flag :boolean(1) default(TRUE), not null # private_flag :boolean(1) default(TRUE), not null
# code :string(255) # code :string(255)
# owner_id :integer(4) # owner_id :integer(4)
# default_branch :string(255) default("master"), not null # default_branch :string(255)
# issues_enabled :boolean(1) default(TRUE), not null # issues_enabled :boolean(1) default(TRUE), not null
# wall_enabled :boolean(1) default(TRUE), not null # wall_enabled :boolean(1) default(TRUE), not null
# merge_requests_enabled :boolean(1) default(TRUE), not null # merge_requests_enabled :boolean(1) default(TRUE), not null
......
class ProtectedBranch < ActiveRecord::Base class ProtectedBranch < ActiveRecord::Base
include GitHost
belongs_to :project belongs_to :project
validates_presence_of :project_id validates_presence_of :project_id
validates_presence_of :name validates_presence_of :name
...@@ -7,7 +9,7 @@ class ProtectedBranch < ActiveRecord::Base ...@@ -7,7 +9,7 @@ class ProtectedBranch < ActiveRecord::Base
after_destroy :update_repository after_destroy :update_repository
def update_repository def update_repository
Gitlab::GitHost.system.update_project(project.path, project) git_host.update_repository(project)
end end
def commit def commit
......
...@@ -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
# #
class UsersProject < ActiveRecord::Base class UsersProject < ActiveRecord::Base
include GitHost
GUEST = 10 GUEST = 10
REPORTER = 20 REPORTER = 20
DEVELOPER = 30 DEVELOPER = 30
...@@ -12,12 +14,29 @@ class UsersProject < ActiveRecord::Base ...@@ -12,12 +14,29 @@ class UsersProject < ActiveRecord::Base
after_save :update_repository after_save :update_repository
after_destroy :update_repository after_destroy :update_repository
validates_uniqueness_of :user_id, scope: [:project_id] validates_uniqueness_of :user_id, scope: [:project_id], message: "already exists in project"
validates_presence_of :user_id validates_presence_of :user_id
validates_presence_of :project_id validates_presence_of :project_id
delegate :name, :email, to: :user, prefix: true delegate :name, :email, to: :user, prefix: true
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.destroy
end
end
end
def self.bulk_update(project, user_ids, project_access)
UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.project_access = project_access
users_project.save
end
end
end
def self.bulk_import(project, user_ids, project_access) def self.bulk_import(project, user_ids, project_access)
UsersProject.transaction do UsersProject.transaction do
user_ids.each do |user_id| user_ids.each do |user_id|
...@@ -58,9 +77,7 @@ class UsersProject < ActiveRecord::Base ...@@ -58,9 +77,7 @@ class UsersProject < ActiveRecord::Base
end end
def update_repository def update_repository
Gitlab::GitHost.system.new.configure do |c| git_host.update_repository(project)
c.update_project(project.path, project)
end
end end
def project_access_human def project_access_human
......
...@@ -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
......
...@@ -9,8 +9,16 @@ class IssueObserver < ActiveRecord::Observer ...@@ -9,8 +9,16 @@ class IssueObserver < ActiveRecord::Observer
def after_update(issue) def after_update(issue)
send_reassigned_email(issue) if issue.is_being_reassigned? send_reassigned_email(issue) if issue.is_being_reassigned?
Note.create_status_change_note(issue, current_user, 'closed') if issue.is_being_closed?
Note.create_status_change_note(issue, current_user, 'reopened') if issue.is_being_reopened? status = nil
status = 'closed' if issue.is_being_closed?
status = 'reopened' if issue.is_being_reopened?
if status
Note.create_status_change_note(issue, current_user, status)
[issue.author, issue.assignee].compact.each do |recipient|
Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user)
end
end
end end
protected protected
......
class KeyObserver < ActiveRecord::Observer class KeyObserver < ActiveRecord::Observer
include GitHost
def after_save(key) def after_save(key)
key.update_repository git_host.set_key(key.identifier, key.key, key.projects)
end end
def after_destroy(key) def after_destroy(key)
key.repository_delete_key return if key.is_deploy_key && !key.last_deploy?
git_host.remove_key(key.identifier, key.projects)
end end
end end
...@@ -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
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
module Account module Account
# Returns a string for use as a Gitolite user identifier
#
# Note that Gitolite 2.x requires the following pattern for users:
#
# ^@?[0-9a-zA-Z][0-9a-zA-Z._\@+-]*$
def identifier def identifier
email.gsub /[^[:alnum:]]/, "_" # Replace non-word chars with underscores, then make sure it starts with
# valid chars
email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
end end
def is_admin? def is_admin?
......
module GitHost
def git_host
Gitlab::Gitolite.new
end
end
...@@ -16,7 +16,7 @@ module IssueCommonality ...@@ -16,7 +16,7 @@ module IssueCommonality
validates :title, validates :title,
presence: true, presence: true,
length: { within: 0..255 } length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] }
scope :opened, where(closed: false) scope :opened, where(closed: false)
scope :closed, where(closed: true) scope :closed, where(closed: true)
......
...@@ -90,6 +90,8 @@ module PushEvent ...@@ -90,6 +90,8 @@ module PushEvent
def push_with_commits? def push_with_commits?
md_ref? && commits.any? && parent_commit && last_commit md_ref? && commits.any? && parent_commit && last_commit
rescue Grit::NoSuchPathError
false
end end
def last_push_to_non_root? def last_push_to_non_root?
......
# Includes methods for handling Git Push events
#
# Triggered by PostReceive job
module PushObserver module PushObserver
def observe_push(oldrev, newrev, ref, user) def observe_push(oldrev, newrev, ref, user)
data = post_receive_data(oldrev, newrev, ref, user) data = post_receive_data(oldrev, newrev, ref, user)
...@@ -84,11 +87,10 @@ module PushObserver ...@@ -84,11 +87,10 @@ module PushObserver
data data
end end
# This method will be called after each post receive and only if the provided
# This method will be called after each post receive # user is present in GitLab.
# and only if user present in gitlab.
# All callbacks for post receive should be placed here
# #
# All callbacks for post receive should be placed here.
def trigger_post_receive(oldrev, newrev, ref, user) def trigger_post_receive(oldrev, newrev, ref, user)
# Create push event # Create push event
self.observe_push(oldrev, newrev, ref, user) self.observe_push(oldrev, newrev, ref, user)
...@@ -101,5 +103,11 @@ module PushObserver ...@@ -101,5 +103,11 @@ module PushObserver
# Create satellite # Create satellite
self.satellite.create unless self.satellite.exists? self.satellite.create unless self.satellite.exists?
# Discover the default branch, but only if it hasn't already been set to
# something else
if default_branch.nil?
update_attributes(default_branch: discover_default_branch)
end
end end
end end
module Repository module Repository
include GitHost
def valid_repo? def valid_repo?
repo repo
rescue rescue
...@@ -6,6 +8,10 @@ module Repository ...@@ -6,6 +8,10 @@ module Repository
false false
end end
def empty_repo?
!repo_exists? || !has_commits?
end
def commit(commit_id = nil) def commit(commit_id = nil)
Commit.find_or_first(repo, commit_id, root_ref) Commit.find_or_first(repo, commit_id, root_ref)
end end
...@@ -48,7 +54,7 @@ module Repository ...@@ -48,7 +54,7 @@ module Repository
end end
def url_to_repo def url_to_repo
Gitlab::GitHost.url_to_repo(path) git_host.url_to_repo(path)
end end
def path_to_repo def path_to_repo
...@@ -56,11 +62,11 @@ module Repository ...@@ -56,11 +62,11 @@ module Repository
end end
def update_repository def update_repository
Gitlab::GitHost.system.update_project(path, self) git_host.update_repository(self)
end end
def destroy_repository def destroy_repository
Gitlab::GitHost.system.destroy_project(self) git_host.remove_repository(self)
end end
def repo_exists? def repo_exists?
...@@ -73,6 +79,14 @@ module Repository ...@@ -73,6 +79,14 @@ module Repository
@heads ||= repo.heads @heads ||= repo.heads
end end
def branches_names
heads.map(&:name)
end
def ref_names
[branches_names + tags].flatten
end
def tree(fcommit, path = nil) def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head fcommit = commit if fcommit == :head
tree = fcommit.tree tree = fcommit.tree
...@@ -88,6 +102,24 @@ module Repository ...@@ -88,6 +102,24 @@ module Repository
end.sort_by(&:name) end.sort_by(&:name)
end end
# Discovers the default branch based on the repository's available branches
#
# - If no branches are present, returns nil
# - If one branch is present, returns its name
# - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch
branches = heads.collect(&:name)
if branches.length == 0
nil
elsif branches.length == 1
branches.first
else
branches.select { |v| v == root_ref }.first
end
end
def has_commits? def has_commits?
!!commit !!commit
end end
...@@ -96,7 +128,7 @@ module Repository ...@@ -96,7 +128,7 @@ module Repository
default_branch || "master" default_branch || "master"
end end
def root_ref? branch def root_ref?(branch)
root_ref == branch root_ref == branch
end end
...@@ -105,7 +137,7 @@ module Repository ...@@ -105,7 +137,7 @@ module Repository
# Already packed repo archives stored at # Already packed repo archives stored at
# app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz # app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
# #
def archive_repo ref def archive_repo(ref)
ref = ref || self.root_ref ref = ref || self.root_ref
commit = self.commit(ref) commit = self.commit(ref)
return nil unless commit return nil unless commit
...@@ -115,10 +147,13 @@ module Repository ...@@ -115,10 +147,13 @@ module Repository
storage_path = File.join(Rails.root, "tmp", "repositories", self.code) storage_path = File.join(Rails.root, "tmp", "repositories", self.code)
file_path = File.join(storage_path, file_name) file_path = File.join(storage_path, file_name)
# Put files into a directory before archiving
prefix = self.code + "/"
# Create file if not exists # Create file if not exists
unless File.exists?(file_path) unless File.exists?(file_path)
FileUtils.mkdir_p storage_path FileUtils.mkdir_p storage_path
file = self.repo.archive_to_file(ref, nil, file_path) file = self.repo.archive_to_file(ref, prefix, file_path)
end end
file_path file_path
...@@ -129,6 +164,6 @@ module Repository ...@@ -129,6 +164,6 @@ module Repository
end end
def http_url_to_repo def http_url_to_repo
http_url = [Gitlab.config.url, "/", path, ".git"].join() http_url = [Gitlab.config.url, "/", path, ".git"].join('')
end end
end end
module SshKey
def update_repository
Gitlab::GitHost.system.new.configure do |c|
c.update_keys(identifier, key)
c.update_projects(projects)
end
end
def repository_delete_key
Gitlab::GitHost.system.new.configure do |c|
#delete key file is there is no identically deploy keys
if !is_deploy_key || Key.where(identifier: identifier).count() == 0
c.delete_key(identifier)
end
c.update_projects(projects)
end
end
end
# Provides an ActiveRecord-like interface to a model whose data is not persisted to a database.
module StaticModel
extend ActiveSupport::Concern
module ClassMethods
# Used by ActiveRecord's polymorphic association to set object_id
def primary_key
'id'
end
# Used by ActiveRecord's polymorphic association to set object_type
def base_class
self
end
end
# Used by AR for fetching attributes
#
# Pass it along if we respond to it.
def [](key)
send(key) if respond_to?(key)
end
def to_param
id
end
def persisted?
false
end
def destroyed?
false
end
end
...@@ -36,4 +36,17 @@ module Team ...@@ -36,4 +36,17 @@ module Team
UsersProject.bulk_import(self, users_ids, access_role) UsersProject.bulk_import(self, users_ids, access_role)
self.update_repository self.update_repository
end end
# Update multiple project users
# to same access role by user ids
def update_users_ids_to_role(users_ids, access_role)
UsersProject.bulk_update(self, users_ids, access_role)
self.update_repository
end
# Delete multiple users from project by user ids
def delete_users_ids_from_team(users_ids)
UsersProject.bulk_delete(self, users_ids)
self.update_repository
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
...@@ -35,11 +35,13 @@ ...@@ -35,11 +35,13 @@
%h3 Latest projects %h3 Latest projects
%hr %hr
- @projects.each do |project| - @projects.each do |project|
%h5 %p
= link_to project.name, [:admin, project] = link_to project.name, [:admin, project]
.span6 .span6
%h3 Latest users %h3 Latest users
%hr %hr
- @users.each do |user| - @users.each do |user|
%h5 %p
= link_to user.name, [:admin, user] = link_to [:admin, user] do
= user.name
%small= user.email
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
Hooks Hooks
%small (#{@hooks.count}) %small (#{@hooks.count})
%br %br
%table.admin-table %table
%tr %tr
%th URL %th URL
%th Method %th Method
......
.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
...@@ -10,19 +10,17 @@ ...@@ -10,19 +10,17 @@
Project name is Project name is
.input .input
= f.text_field :name, placeholder: "Example Project", class: "xxlarge" = f.text_field :name, placeholder: "Example Project", class: "xxlarge"
= f.submit project.new_record? ? 'Create project' : 'Save Project', class: "btn primary"
%hr %hr
.alert.alert-info .adv_settings
%h5 Advanced settings: %h6 Advanced settings:
.clearfix .clearfix
= f.label :path do = f.label :path do
Git Clone Path
.input .input
.input-prepend .input-prepend
%span.add-on= Gitlab.config.ssh_path %strong
= f.text_field :path, placeholder: "example_project", disabled: !!project.id = text_field_tag :ppath, @admin_project.path_to_repo, class: "xlarge", disabled: true
%span.add-on= ".git"
.clearfix .clearfix
= f.label :code do = f.label :code do
URL URL
...@@ -34,7 +32,7 @@ ...@@ -34,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
...@@ -42,8 +40,9 @@ ...@@ -42,8 +40,9 @@
.input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;") .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;")
- unless project.new_record? - unless project.new_record?
.alert.alert-info %hr
%h5 Features: .adv_settings
%h6 Features:
.clearfix .clearfix
= f.label :issues_enabled, "Issues" = f.label :issues_enabled, "Issues"
...@@ -63,13 +62,13 @@ ...@@ -63,13 +62,13 @@
- unless project.new_record? - unless project.new_record?
.actions .actions
= f.submit 'Save Project', class: "btn primary" = f.submit 'Save Project', class: "btn save-btn"
= link_to 'Cancel', admin_projects_path, class: "btn cancel-btn"
:javascript :javascript
$(function(){ $(function(){
$('#project_owner_id').chosen();
new Projects(); new Projects();
}) })
= form_for [:admin, @admin_project] do |f|
- if @admin_project.errors.any?
.alert-message.block-message.error
%span= @admin_project.errors.full_messages.first
.clearfix.project_name_holder
= f.label :name do
Project name is
.input
= f.text_field :name, placeholder: "Example Project", class: "xxlarge"
= f.submit 'Create project', class: "btn primary project-submit"
%hr
%div.adv_settings
%h6 Advanced settings:
.clearfix
= f.label :path do
Git Clone
.input
.input-prepend
%span.add-on= Gitlab.config.ssh_path
= f.text_field :path, placeholder: "example_project", disabled: !@admin_project.new_record?
%span.add-on= ".git"
.clearfix
= f.label :code do
URL
.input
.input-prepend
%span.add-on= web_app_url
= f.text_field :code, placeholder: "example"
%h3 %h3.page_title
Projects Projects
= link_to 'New Project', new_admin_project_path, class: "btn small right" = link_to 'New Project', new_admin_project_path, class: "btn small right"
%br %br
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
= text_field_tag :name, params[:name], class: "xlarge" = text_field_tag :name, params[:name], class: "xlarge"
= submit_tag "Search", class: "btn submit primary" = submit_tag "Search", class: "btn submit primary"
%table.admin-table %table
%thead %thead
%th Name %th Name
%th Path %th Path
......
%h3.page_title New project .project_new_holder
%hr %h3.page_title
= render 'form', project: @admin_project New Project
%hr
= render 'new_form'
%div.save-project-loader.hide
%center
= image_tag "ajax_loader.gif"
%h3 Creating project &amp; repository. Please wait a few minutes
:javascript
$(function(){ new Projects(); });
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
= link_to 'Edit', edit_admin_project_path(@admin_project), class: "btn right small" = link_to 'Edit', edit_admin_project_path(@admin_project), class: "btn right small"
%br %br
%table.zebra-striped.table-bordered %table.zebra-striped
%thead
%tr
%th Project
%th
%tr %tr
%td %td
%b %b
...@@ -40,7 +44,7 @@ ...@@ -40,7 +44,7 @@
%small %small
(#{@admin_project.users_projects.count}) (#{@admin_project.users_projects.count})
%br %br
%table.zebra-striped.table-bordered %table.zebra-striped
%thead %thead
%tr %tr
%th Name %th Name
...@@ -60,32 +64,18 @@ ...@@ -60,32 +64,18 @@
%h3 Add new team member %h3 Add new team member
%br %br
= form_tag team_update_admin_project_path(@admin_project), class: "bulk_import", method: :put do = form_tag team_update_admin_project_path(@admin_project), class: "bulk_import", method: :put do
%table.zebra-striped.table-bordered %table.zebra-striped
%thead %thead
%tr %tr
%th Users %th Users
%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: "/info/resque", width: 1168, height: 600, style: "border: none"} %br
\ No newline at end of file .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();
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
= form_for [:admin, @admin_user] do |f| = form_for [:admin, @admin_user] do |f|
-if @admin_user.errors.any? -if @admin_user.errors.any?
#error_explanation #error_explanation
%ul %ul.unstyled.alert.alert-error
- @admin_user.errors.full_messages.each do |msg| - @admin_user.errors.full_messages.each do |msg|
%li= msg %li= msg
.row .row
.span6 .span7
.ui-box
%br
.clearfix .clearfix
= f.label :name = f.label :name
.input .input
...@@ -19,12 +21,11 @@ ...@@ -19,12 +21,11 @@
= f.text_field :email = f.text_field :email
%span.help-inline * required %span.help-inline * required
%hr %hr
-if f.object.new_record? -if f.object.new_record?
.clearfix .clearfix
= f.label :admin, class: "checkbox" do = f.label :force_random_password do
= f.check_box :force_random_password, {}, true, nil
%span Generate random password %span Generate random password
.input= f.check_box :force_random_password, {}, true, nil
%div.password-fields %div.password-fields
.clearfix .clearfix
...@@ -43,30 +44,37 @@ ...@@ -43,30 +44,37 @@
.clearfix .clearfix
= f.label :twitter = f.label :twitter
.input= f.text_field :twitter .input= f.text_field :twitter
.span6 .span5
.ui-box
%br
.clearfix .clearfix
= f.label :projects_limit = f.label :projects_limit
.input= f.text_field :projects_limit, class: "small_input" .input= f.number_field :projects_limit
.alert
.clearfix .clearfix
%p Make the user a GitLab administrator. = f.label :admin do
= f.label :admin, class: "checkbox" do %strong.cred Administrator
= f.check_box :admin .input= f.check_box :admin
%span Administrator
- unless @admin_user.new_record? - unless @admin_user.new_record?
.alert.alert-error %hr
.padded.cred
- if @admin_user.blocked - if @admin_user.blocked
%span %span
= link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn small"
This user is blocked and is not able to login to GitLab This user is blocked and is not able to login to GitLab
.clearfix
= link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn small right"
- else - else
%span %span
= link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"
Blocked users will be removed from all projects &amp; will not be able to login to GitLab. Blocked users will be removed from all projects &amp; will not be able to login to GitLab.
.clearfix
= link_to 'Block User', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small right danger"
.row
.span6
.span6
.actions .actions
= f.submit 'Save', class: "btn primary" = f.submit 'Save', class: "btn save-btn"
- if @admin_user.new_record? - if @admin_user.new_record?
= link_to 'Cancel', admin_users_path, class: "btn" = link_to 'Cancel', admin_users_path, class: "btn cancel-btn"
- else - else
= link_to 'Cancel', admin_user_path(@admin_user), class: "btn" = link_to 'Cancel', admin_user_path(@admin_user), class: "btn cancel-btn"
%h3= @admin_user.name %h3.page_title #{@admin_user.name} &rarr; Edit user
%hr %hr
= render 'form' = render 'form'
%h3 %h3.page_title
Users Users
= link_to 'New User', new_admin_user_path, class: "btn small right" = link_to 'New User', new_admin_user_path, class: "btn small right"
%br %br
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
= link_to admin_users_path(filter: "wop") do = link_to admin_users_path(filter: "wop") do
Without projects Without projects
%table.admin-table %table
%thead %thead
%th Admin %th Admin
%th Name %th Name
......
%h2 New user %h3.page_title New user
%hr %br
= render 'form' = render 'form'
...@@ -8,7 +8,11 @@ ...@@ -8,7 +8,11 @@
%br %br
%table.zebra-striped.table-bordered %table.zebra-striped
%thead
%tr
%th Profile
%th
%tr %tr
%td %td
%b %b
...@@ -57,15 +61,15 @@ ...@@ -57,15 +61,15 @@
%h3 Add User to Projects %h3 Add User to Projects
%br %br
= form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do = form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do
%table.table-bordered %table
%thead %thead
%tr %tr
%th Projects %th Projects
%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"
...@@ -78,9 +82,9 @@ ...@@ -78,9 +82,9 @@
%h3 Projects %h3 Projects
%br %br
%table.zebra-striped.table-bordered %table.zebra-striped
%tr
%thead %thead
%tr
%th Name %th Name
%th Project Access %th Project Access
%th %th
...@@ -93,17 +97,3 @@ ...@@ -93,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();
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
%strong= link_to "Browse Code »", tree_project_ref_path(@project, commit.id), class: "right" %strong= link_to "Browse Code »", tree_project_ref_path(@project, commit.id), class: "right"
%p %p
= link_to commit.short_id(8), project_commit_path(@project, id: commit.id), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(@project, id: commit.id), class: "commit_short_id"
%strong.cgray= commit.author_name %strong.commit-author-name= commit.author_name
&ndash; %span.dash &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, id: commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, id: commit.id), class: "row_title"
......
...@@ -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 "head" = render "head"
%h3 %h3.page_title
Compare View Compare View
%hr %hr
%div %div
%p %p.slead
Fill input field with commit id like Fill input field with commit id like
%code '4eedf23' %code.label_branch 4eedf23
or branch/tag name like or branch/tag name like
%code master %code.label_branch master
&amp; press compare button for commits list, code diff. and press compare button for commits list, code diff.
%br %br
...@@ -19,22 +19,24 @@ ...@@ -19,22 +19,24 @@
= text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
= "..." = "..."
= text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge" = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
- if @refs_are_same
.alert
%span Refs are the same
.actions .actions
= submit_tag "Compare", class: "btn btn-primary" = submit_tag "Compare", class: "btn primary wide commits-compare-btn"
- if @commits.present?
- unless @commits.empty?
%div.ui-box %div.ui-box
%h5.small Commits (#{@commits.count}) %h5.small Commits (#{@commits.count})
%ul.unstyled= render @commits %ul.unstyled= render @commits
- unless @diffs.empty? - unless @diffs.empty?
%h4 Diff %h4 Diff
= render "commits/diffs", diffs: @diffs = render "commits/diffs", diffs: @diffs
:javascript :javascript
$(function() { $(function() {
var availableTags = #{@project.heads.map(&:name).to_json}; var availableTags = #{@project.ref_names.to_json};
$("#from").autocomplete({ $("#from").autocomplete({
source: availableTags, source: availableTags,
...@@ -45,5 +47,7 @@ ...@@ -45,5 +47,7 @@
source: availableTags, source: availableTags,
minLength: 1 minLength: 1
}); });
disableButtonIfEmptyField('#to', '.commits-compare-btn');
}); });
= 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"
:javascript :javascript
$(document).ready(function(){ $(function(){
$(".line_note_link, .line_note_reply_link").live("click", function(e) { PerLineNotes.init();
var form = $(".per_line_form");
$(this).parent().parent().after(form);
form.find("#note_line_code").val($(this).attr("line_code"));
form.show();
return false;
});
}); });
...@@ -19,22 +19,31 @@ ...@@ -19,22 +19,31 @@
= link_to new_project_path, class: "btn very_small info" do = link_to new_project_path, class: "btn very_small info" do
%i.icon-plus %i.icon-plus
New Project New Project
%ul.unstyled
- @projects.each do |project| - @projects.each do |project|
%li.wll
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
%h4 %strong.project_name= truncate(project.name, length: 25)
%span.ico.project %span.arrow
= truncate(project.name, length: 25)
%span.right
&rarr; &rarr;
%span.last_activity
%strong Last activity:
%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
......
...@@ -11,8 +11,13 @@ ...@@ -11,8 +11,13 @@
.input= f.text_field :title .input= f.text_field :title
.clearfix .clearfix
= f.label :key = f.label :key
.input= f.text_area :key, class: "xlarge" .input
= f.text_area :key, class: [:xxlarge, :thin_area]
%p.hint
Paste a machine public key here. Read more about how generate it
= link_to "here", help_ssh_path
.actions .actions
= f.submit 'Save', class: "primary btn" = f.submit 'Save', class: "save-btn btn"
= link_to "Cancel", project_deploy_keys_path(@project), class: "btn" = link_to "Cancel", project_deploy_keys_path(@project), class: "btn cancel-btn"
= render "repositories/head" = render "repositories/head"
- if can? current_user, :admin_project, @project
.alert-message.block-message %p.slead
Deploy keys allow read-only access to repository. Deploy keys allow read-only access to repository. It matches perfectly for CI, staging or production servers.
- if can? current_user, :admin_project, @project
= link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do = link_to new_project_deploy_key_path(@project), class: "btn small", title: "New Deploy Key" do
Add Deploy Key Add Deploy Key
- if @keys.any? - if @keys.any?
%table %table
%thead
%tr
%th Keys
%th
%th
- @keys.each do |key| - @keys.each do |key|
= render(partial: 'show', locals: {key: key}) = render(partial: 'show', locals: {key: key})
= render "repositories/head" = render "repositories/head"
%h3 New Deploy key %h3.page_title New Deploy key
%hr %hr
= render 'form' = render 'form'
= render "repositories/head" = render "repositories/head"
%h3= @key.title %h3.page_title
Deploy key:
= @key.title
%small
created at
= @key.created_at.stamp("Aug 21, 2011")
.back_link
= link_to project_deploy_keys_path(@project) do
&larr; To keys list
%hr %hr
%pre= @key.key %pre= @key.key
.actions .right
= link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key" = link_to 'Remove', project_deploy_key_path(@key.project, @key), confirm: 'Are you sure?', method: :delete, class: "danger btn delete-key"
.clear
<%= form_tag(user_omniauth_callback_path(:ldap), :class => "login-box", :id => 'new_ldap_user' ) do %>
<%= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" %>
<%= text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"} %>
<%= password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"} %>
<br/>
<%= submit_tag "LDAP Sign in", :class => "primary btn" %>
<%- if devise_mapping.omniauthable? %>
<%- (resource_class.omniauth_providers - [:ldap]).each do |provider| %>
<hr/>
<%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" %><br />
<% end -%>
<% end -%>
<hr/>
<a href="#" id="other_form_toggle" onclick="javascript:$('#new_user').toggle();">Other Sign in</a>
<!-- inline for right now just to illustrate -->
<script type="text/javascript">
$(function() {
$('#new_user').toggle();
});
</script>
<% end %>
<%= 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? -%>
<div class="clearfix inputs-list"> <label class="checkbox remember_me" for="user_remember_me"><%= f.check_box :remember_me %><span>Remember me</span></label></div>
<% end -%>
<br/>
<%= f.submit "Sign in", :class => "primary btn" %>
<div class="right"> <%= render :partial => "devise/shared/links" %></div>
<% end %>
= form_tag(user_omniauth_callback_path(:ldap), :class => "login-box", :id => 'new_ldap_user' ) do
= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
= text_field_tag :username, nil, {:class => "text top", :placeholder => "LDAP Login"}
= password_field_tag :password, nil, {:class => "text bottom", :placeholder => "Password"}
%br/
= submit_tag "LDAP Sign in", :class => "primary btn"
- if devise_mapping.omniauthable?
- (resource_class.omniauth_providers - [:ldap]).each do |provider|
%hr/
= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary"
%br/
%hr/
%a#other_form_toggle{:href => "#", :onclick => "javascript:$('#new_user').toggle();"} Other Sign in
:javascript
$(function() {
$('#new_user').toggle();
});
= 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?
.clearfix.inputs-list
%label.checkbox.remember_me{:for => "user_remember_me"}
= f.check_box :remember_me
%span Remember me
%br/
= f.submit "Sign in", :class => "primary btn"
.right
= render :partial => "devise/shared/links"
<% unless ldap_enable? -%>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f| %>
<%= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo" %>
<%= f.text_field :email, :class => "text top", :placeholder => "Email" %>
<%= f.password_field :password, :class => "text bottom", :placeholder => "Password" %>
<% if devise_mapping.rememberable? -%>
<div class="clearfix inputs-list"> <label class="checkbox remember_me" for="user_remember_me"><%= f.check_box :remember_me %><span>Remember me</span></label></div>
<% end -%>
<br/>
<%= f.submit "Sign in", :class => "primary btn" %>
<div class="right"> <%= render :partial => "devise/shared/links" %></div>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<hr/>
<%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" %><br />
<% end -%>
<% end -%>
<% end %>
<% else %>
<%= render :partial => 'devise/sessions/new_ldap' %>
<% end %>
- if ldap_enable?
= render :partial => 'devise/sessions/new_ldap'
- else
= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
= f.text_field :email, :class => "text top", :placeholder => "Email"
= f.password_field :password, :class => "text bottom", :placeholder => "Password"
- if devise_mapping.rememberable?
.clearfix.inputs-list
%label.checkbox.remember_me{:for => "user_remember_me"}
= f.check_box :remember_me
%span Remember me
%br/
= f.submit "Sign in", :class => "primary btn wide"
.right
= render :partial => "devise/shared/links"
- if devise_mapping.omniauthable?
%hr/
- resource_class.omniauth_providers.each do |provider|
%span
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
.alert-message.block-message.error %h1 Encoding Error
%h3 Encoding Error %hr
%hr %p Page can't be loaded because of an encoding error.
%p
Page can't be loaded because of an encoding error.
%h1 Git Error %h1 Git Error
%hr %hr
%h2 Gitlab was unable to access your Gitolite system. %h2 GitLab was unable to access your Gitolite system.
.git_error_tips .git_error_tips
%h4 Tips for Administrator: %h4 Tips for Administrator:
...@@ -21,5 +21,5 @@ ...@@ -21,5 +21,5 @@
Permissions: Permissions:
%pre %pre
= preserve do = preserve do
sudo chmod -R 770 /home/git/repositories/ sudo chmod -R 770 #{Gitlab.config.git_base_path}
sudo chown -R git:git /home/git/repositories/ sudo chown -R git:git #{Gitlab.config.git_base_path}
...@@ -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"
...@@ -11,3 +11,7 @@ ...@@ -11,3 +11,7 @@
.event_feed .event_feed
= render "events/event_push", event: event = render "events/event_push", event: event
- elsif event.membership_changed?
.event_feed
= render "events/event_membership_changed", event: event
...@@ -9,5 +9,5 @@ ...@@ -9,5 +9,5 @@
at at
%strong= link_to event.project.name, event.project %strong= link_to event.project.name, event.project
= link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn very_small primary" do = link_to new_mr_path_from_push_event(event), title: "New Merge Request", class: "btn very_small" do
Create Merge Request Create Merge Request
= 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.
%h3 API %h3.page_title API
.back_link .back_link
= link_to help_path do = link_to help_path do
&larr; to index &larr; to index
%hr %br
%ol %ul.nav.nav-tabs.log-tabs
%li.active
= link_to "README", "#README", 'data-toggle' => 'tab'
%li
= link_to "Projects", "#projects", 'data-toggle' => 'tab'
%li
= link_to "Snippets", "#snippets", 'data-toggle' => 'tab'
%li
= link_to "Repositories", "#repositories", 'data-toggle' => 'tab'
%li %li
%a{href: "#README"} README = link_to "Users", "#users", 'data-toggle' => 'tab'
%li %li
%a{href: "#projects"} Projects = link_to "Session", "#session", 'data-toggle' => 'tab'
%li %li
%a{href: "#users"} Users = link_to "Issues", "#issues", 'data-toggle' => 'tab'
%li %li
%a{href: "#issues"} Issues = link_to "Milestones", "#milestones", 'data-toggle' => 'tab'
.file_holder#README .tab-content
.tab-pane.active#README
.file_holder
.file_title .file_title
%i.icon-file %i.icon-file
README README
...@@ -22,9 +32,8 @@ ...@@ -22,9 +32,8 @@
= preserve do = preserve do
= markdown File.read(Rails.root.join("doc", "api", "README.md")) = markdown File.read(Rails.root.join("doc", "api", "README.md"))
%br .tab-pane#projects
.file_holder
.file_holder#projects
.file_title .file_title
%i.icon-file %i.icon-file
Projects Projects
...@@ -32,9 +41,26 @@ ...@@ -32,9 +41,26 @@
= preserve do = preserve do
= markdown File.read(Rails.root.join("doc", "api", "projects.md")) = markdown File.read(Rails.root.join("doc", "api", "projects.md"))
%br .tab-pane#snippets
.file_holder
.file_title
%i.icon-file
Projects Snippets
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "snippets.md"))
.file_holder#users .tab-pane#repositories
.file_holder
.file_title
%i.icon-file
Projects
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "repositories.md"))
.tab-pane#users
.file_holder
.file_title .file_title
%i.icon-file %i.icon-file
Users Users
...@@ -42,12 +68,29 @@ ...@@ -42,12 +68,29 @@
= preserve do = preserve do
= markdown File.read(Rails.root.join("doc", "api", "users.md")) = markdown File.read(Rails.root.join("doc", "api", "users.md"))
%br .tab-pane#session
.file_holder
.file_title
%i.icon-file
Session
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "session.md"))
.file_holder#issues .tab-pane#issues
.file_holder
.file_title .file_title
%i.icon-file %i.icon-file
Issues Issues
.file_content.wiki .file_content.wiki
= preserve do = preserve do
= markdown File.read(Rails.root.join("doc", "api", "issues.md")) = markdown File.read(Rails.root.join("doc", "api", "issues.md"))
.tab-pane#milestones
.file_holder
.file_title
%i.icon-file
Milestones
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "milestones.md"))
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
%h5= link_to "API", help_api_path %h5= link_to "API", help_api_path
%li %li
%h5= link_to "Gitlab Markdown", help_markdown_path %h5= link_to "GitLab Markdown", help_markdown_path
%li %li
%h5= link_to "SSH keys", help_ssh_path %h5= link_to "SSH keys", help_ssh_path
- bash_lexer = Pygments::Lexer[:bash] %h3.page_title GitLab Flavored Markdown
%h3.page_title Gitlab Markdown
.back_link .back_link
= link_to help_path do = link_to help_path do
&larr; to index &larr; to index
%hr %hr
%p.slead We extend Markdown with some GITLAB specific syntax. It allows you to link to: .row
.span8
%p
For GitLab we developed something we call "GitLab Flavored Markdown" (GFM).
It extends the standard Markdown in a few significant ways adds some useful functionality.
%ul %p You can use GFM in:
%li issues (#123) %ul
%li merge request (!123)
%li commits (1234567)
%li team members (@foo)
%li snippets ($123)
%p.slead in
%ul
%li commit messages %li commit messages
%li notes/comments/wall posts %li comments
%li wall posts
%li issues %li issues
%li merge requests %li merge requests
%li milestones %li milestones
%li wiki pages %li wiki pages
.span4
.alert.alert-info
%p
If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent
%strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax"
at Daring Fireball.
.row
.span8
%h3 Differences from traditional Markdown
%h4 Newlines
%p
The biggest difference that GFM introduces is in the handling of linebreaks.
With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors.
GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended.
%p The next paragraph contains two phrases separated by a single newline character:
%pre= "Roses are red\nViolets are blue"
%p becomes
= markdown "Roses are red\nViolets are blue"
%h4 Multiple underscores in words
%p
It is not reasonable to italicize just <em>part</em> of a word, especially when you're dealing with code and names often appear with multiple underscores.
Therefore, GFM ignores multiple underscores in words.
%pre= "perform_complicated_task\ndo_this_and_do_that_and_another_thing"
%p becomes
= markdown "perform_complicated_task\ndo_this_and_do_that_and_another_thing"
%h4 URL autolinking
%p
GFM will autolink standard URLs you copy and paste into your text.
So if you want to link to a URL (instead of a textual link), you can simply put the URL in verbatim and it will be turned into a link to that URL.
%h4 Fenced code blocks
%p
Markdown converts text with four spaces at the front of each line to code blocks.
GFM supports that, but we also support fenced blocks.
Just wrap your code blocks in <code>```</code> and you won't need to indent manually to trigger a code block.
%pre= %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```}
%p becomes
= markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```}
%h4 Emoji
.row
.span8
:ruby
puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you:
:exclamation: You can use emoji anywhere GFM is supported. :sunglasses:
You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that.
If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
}
.span4
.alert.alert-info
%p
Consult the
%strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/"
for a list of all supported emoji codes.
.row
.span8
%h4 Special GitLab references
%p
GFM recognizes special references.
You can easily reference e.g. a team member, an issue or a commit within a project.
GFM will turn that reference into a link so you can navigate between them easily.
%p GFM will recognize the following references:
%ul
%li
%code @foo
for team members
%li
%code #123
for issues
%li
%code !123
for merge request
%li
%code $123
for snippets
%li
%code 1234567
for commits
-# this example will only be shown if the user has a project with at least one issue
- if @project = current_user.projects.first
- if issue = @project.issues.first
%p For example in your #{link_to @project.name, project_path(@project)} project, writing:
%pre= "This is related to ##{issue.id}. @#{current_user.name} is working on solving it."
%p becomes:
= markdown "This is related to ##{issue.id}. @#{current_user.name} is working on solving it."
- @project = nil # Prevent this from bubbling up to page title
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%hr %hr
%p.slead %p.slead
SSH key allows you to establish a secure connection between your computer and Gitlab SSH key allows you to establish a secure connection between your computer and GitLab
%p.slead %p.slead
To generate a new SSH key just open your terminal and use code below. To generate a new SSH key just open your terminal and use code below.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
\# Generating public/private rsa key pair... \# Generating public/private rsa key pair...
%p.slead %p.slead
Next just use code below to dump your public key and add to GITLAB SSH Keys Next just use code below to dump your public key and add to GitLab SSH Keys
%pre.dark %pre.dark
cat ~/.ssh/id_rsa.pub cat ~/.ssh/id_rsa.pub
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%hr %hr
%p.slead %p.slead
Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member. Your GitLab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
%br %br
System Hooks can be used for logging or change information in LDAP server. System Hooks can be used for logging or change information in LDAP server.
%br %br
......
...@@ -5,11 +5,11 @@ ...@@ -5,11 +5,11 @@
%hr %hr
%p.slead %p.slead
Every Gitlab project can trigger a web server whenever the repo is pushed to. Every GitLab project can trigger a web server whenever the repo is pushed to.
%br %br
Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.
%br %br
GITLAB will send POST request with commits information on every push. GitLab will send POST request with commits information on every push.
%h5 Hooks request example: %h5 Hooks request example:
= render "hooks/data_ex" = render "hooks/data_ex"
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