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
db/data.yml
.idea
.DS_Store
.chef
......@@ -9,6 +9,8 @@ branches:
- 'master'
rvm:
- 1.9.3
services:
- mysql
before_script:
- "cp config/database.yml.$DB config/database.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
- ability to disable gravatars
- 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"
def darwin_only(require_as)
RUBY_PLATFORM.include?('darwin') && require_as
end
def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "3.2.8"
# Supported DBs
......@@ -8,6 +16,10 @@ gem "mysql2"
# Auth
gem "devise", "~> 2.1.0"
gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
# GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
......@@ -45,6 +57,7 @@ gem "seed-fu"
# Markdown to HTML
gem "redcarpet", "~> 2.1.1"
gem "github-markup", "~> 0.7.4"
# Servers
gem "thin"
......@@ -97,20 +110,28 @@ group :development do
end
group :development, :test do
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara"
gem "capybara-webkit"
gem "headless"
gem "autotest"
gem "autotest-rails"
gem "pry"
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
gem 'factory_girl_rails'
# Guard
gem 'guard-rspec'
gem 'guard-spinach'
# Notification
gem 'rb-fsevent', :require => darwin_only('rb-fsevent')
gem 'growl', :require => darwin_only('growl')
gem 'rb-inotify', :require => linux_only('rb-inotify')
end
group :test do
gem 'cucumber-rails', :require => false
gem "simplecov", :require => false
gem "shoulda-matchers"
gem 'email_spec'
......@@ -119,5 +140,5 @@ group :test do
end
group :production do
gem "gitlab_meta", '2.8'
gem "gitlab_meta", '2.9'
end
......@@ -68,7 +68,6 @@ GIT
GEM
remote: http://rubygems.org/
specs:
ZenTest (4.8.1)
actionmailer (3.2.8)
actionpack (= 3.2.8)
mail (~> 2.4.4)
......@@ -100,15 +99,11 @@ GEM
rails (~> 3.0)
addressable (2.2.8)
arel (3.0.2)
autotest (4.4.6)
ZenTest (>= 4.4.1)
autotest-rails (4.1.2)
ZenTest (~> 4.5)
awesome_print (1.0.2)
bcrypt-ruby (3.0.1)
blankslate (2.1.2.4)
bootstrap-sass (2.0.4.0)
builder (3.0.0)
builder (3.0.2)
capybara (1.1.2)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
......@@ -125,7 +120,7 @@ GEM
charlock_holmes (0.6.8)
childprocess (0.3.2)
ffi (~> 1.0.6)
chosen-rails (0.9.8)
chosen-rails (0.9.8.3)
railties (~> 3.0)
thor (~> 0.14)
coderay (1.0.6)
......@@ -137,16 +132,8 @@ GEM
execjs
coffee-script-source (1.3.3)
colored (1.2)
colorize (0.5.8)
crack (0.3.1)
cucumber (1.2.1)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.11.0)
json (>= 1.4.6)
cucumber-rails (1.3.0)
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
nokogiri (>= 1.5.0)
daemons (1.1.8)
database_cleaner (0.8.0)
devise (2.1.2)
......@@ -166,20 +153,36 @@ GEM
eventmachine (0.12.10)
execjs (1.4.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)
ffi (1.0.11)
foreman (0.47.0)
thor (>= 0.13.6)
gherkin (2.11.0)
json (>= 1.4.6)
gherkin-ruby (0.2.1)
git (1.2.5)
gitlab_meta (2.8)
github-markup (0.7.4)
gitlab_meta (2.9)
grape (0.2.1)
hashie (~> 1.2)
multi_json
multi_xml
rack
rack-mount
growl (1.0.3)
guard (1.3.2)
listen (>= 0.4.2)
thor (>= 0.14.6)
guard-rspec (1.2.1)
guard (>= 1.1)
guard-spinach (0.0.2)
guard (>= 1.1)
spinach
haml (3.1.6)
haml-rails (0.3.4)
actionpack (~> 3.0)
......@@ -193,7 +196,8 @@ GEM
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
i18n (0.6.0)
httpauth (0.1)
i18n (0.6.1)
journey (1.0.4)
jquery-rails (2.0.2)
railties (>= 3.2.0, < 5.0)
......@@ -201,11 +205,12 @@ GEM
jquery-ui-rails (0.5.0)
jquery-rails
railties (>= 3.1.0)
json (1.7.4)
kaminari (0.13.0)
json (1.7.5)
jwt (0.1.5)
multi_json (>= 1.0)
kaminari (0.14.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
railties (>= 3.0.0)
kgio (2.7.4)
launchy (2.1.0)
addressable (~> 2.2.6)
......@@ -214,6 +219,7 @@ GEM
libv8 (3.3.10.4)
libwebsocket (0.1.3)
addressable
listen (0.5.0)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
......@@ -224,12 +230,35 @@ GEM
sprockets (~> 2.0)
multi_json (1.3.6)
multi_xml (0.5.1)
multipart-post (1.1.5)
mysql2 (0.3.11)
net-ldap (0.2.2)
nokogiri (1.5.3)
oauth (0.4.7)
oauth2 (0.8.0)
faraday (~> 0.8)
httpauth (~> 0.1)
jwt (~> 0.1.4)
multi_json (~> 1.0)
rack (~> 1.2)
omniauth (1.1.0)
hashie (~> 1.2)
rack
omniauth-github (1.0.3)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-google-oauth2 (0.1.13)
omniauth (~> 1.0)
omniauth-oauth2
omniauth-oauth (1.0.1)
oauth
omniauth (~> 1.0)
omniauth-oauth2 (1.1.0)
oauth2 (~> 0.8.0)
omniauth (~> 1.0)
omniauth-twitter (0.0.13)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
orm_adapter (0.3.0)
polyglot (0.3.3)
posix-spawn (0.3.6)
......@@ -269,6 +298,9 @@ GEM
raindrops (0.9.0)
rake (0.9.2.2)
raphael-rails (1.5.2)
rb-fsevent (0.9.1)
rb-inotify (0.8.8)
ffi (>= 0.5.0)
rdoc (3.12)
json (~> 1.4)
redcarpet (2.1.1)
......@@ -319,7 +351,7 @@ GEM
multi_json (~> 1.0)
rubyzip
settingslogic (2.0.8)
shoulda-matchers (1.1.0)
shoulda-matchers (1.3.0)
activesupport (>= 3.0.0)
simplecov (0.6.4)
multi_json (~> 1.0)
......@@ -331,6 +363,13 @@ GEM
tilt (~> 1.3, >= 1.3.3)
six (0.2.0)
slop (2.4.4)
spinach (0.5.2)
colorize
gherkin-ruby (~> 0.2.0)
spinach-rails (0.1.8)
capybara (~> 1)
railties (>= 3)
spinach (>= 0.4)
sprockets (2.1.3)
hike (~> 1.2)
rack (~> 1.0)
......@@ -343,7 +382,7 @@ GEM
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.15.4)
thor (0.16.0)
tilt (1.3.3)
treetop (1.4.10)
polyglot
......@@ -372,8 +411,6 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on (= 2.3.1)
annotate!
autotest
autotest-rails
awesome_print
bootstrap-sass (= 2.0.4)
capybara
......@@ -383,19 +420,23 @@ DEPENDENCIES
chosen-rails
coffee-rails (= 3.2.2)
colored
cucumber-rails
database_cleaner
devise (~> 2.1.0)
draper
email_spec
factory_girl_rails
ffaker
foreman
git
gitlab_meta (= 2.8)
github-markup (~> 0.7.4)
gitlab_meta (= 2.9)
gitolite!
grack!
grape (~> 0.2.1)
grit!
growl
guard-rspec
guard-spinach
haml-rails
headless
httparty
......@@ -407,12 +448,18 @@ DEPENDENCIES
linguist (~> 1.0.0)!
modernizr (= 2.5.3)
mysql2
omniauth
omniauth-github
omniauth-google-oauth2
omniauth-ldap!
omniauth-twitter
pry
pygments.rb!
rack-mini-profiler
rails (= 3.2.8)
raphael-rails (= 1.5.2)
rb-fsevent
rb-inotify
redcarpet (~> 2.1.1)
resque (~> 1.20.0)
resque_mailer
......@@ -424,6 +471,7 @@ DEPENDENCIES
shoulda-matchers
simplecov
six
spinach-rails
sqlite3
stamp
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
## Contribute
[Development Tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
Want to help - send a pull request.
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 @@
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.waitforimages
//= require bootstrap-modal
//= require bootstrap
//= require modernizr
//= require chosen-jquery
//= require raphael
//= require branch-graph
//= 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){
$('select#issue_milestone_id').chosen();
$("#new_issue_dialog").show("fade", { direction: "right" }, 150);
$('.top-tabs .add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
});
}
......@@ -15,6 +16,7 @@ function switchToEditIssue(form){
$('select#issue_milestone_id').chosen();
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
$('.add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
});
}
......@@ -78,6 +80,10 @@ function issuesPage(){
$(this).closest("form").submit();
});
$("#new_issue_link").click(function(){
updateNewIssueURL();
});
$('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
var t = $(this),
totalIssues,
......@@ -124,3 +130,20 @@ function issuesCheckChanged() {
$('.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 **/
body {
margin-bottom:20px;
}
.container {
padding-top:0;
z-index:5;
......@@ -40,30 +38,6 @@
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 }
.back_link {
......@@ -88,16 +62,6 @@
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 {
position: relative;
top: -2px;
......@@ -129,26 +93,18 @@ table a code {
border-bottom:1px solid #ccc;
h4 {
color:#444;
font-size:22px;
color:#666;
font-size:18px;
line-height:38px;
padding-top:5px;
margin:2px;
font-weight:normal;
}
}
.git_url_wrapper {
margin-right:50px
}
.file_stats {
span {
img {
width:14px;
float:left;
margin-right:6px;
padding:2px 0;
}
}
}
.handle:hover {
cursor:move;
......@@ -172,10 +128,6 @@ span.update-author {
display:block;
}
/** END UPDATE ITEM **/
.ajax-tab-loading {
padding:40px;
display:none;
}
.dashboard-loader {
float:left;
margin:10px;
......@@ -186,15 +138,144 @@ span.update-author {
font-weight:bold;
}
a.project-update.titled {
position:relative;
padding-left:35% !important;
.title-block {
padding:10px;
width:35%;
position:absolute;
left:0;
top:0;
.neib {
margin-right:10px;
}
.label {
background-color: #474D57;
&.label-tag {
background: none;
border: none;
padding:4px 6px;
color:#444;
text-shadow:0 0 1px #fff;
&.grouped {
float: left;
margin-right: 6px;
padding: 6px;
}
}
&.label-issue {
background-color: #eee;
border: 1px solid #ccc;
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 {
}
/**
* 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 {
border:2px solid #ddd;
}
......@@ -393,46 +440,48 @@ p.time {
}
}
.btn {
&.very_small {
font-size:11px;
padding:2px 6px;
margin:2px;
.votes {
font-size: 13px;
line-height: 15px;
.progress {
height: 4px;
margin: 0;
.bar {
float: left;
height: 100%;
}
&.grouped {
margin-right:7px;
float:left;
.bar-success {
background-color: #468847;
@include bg-gradient(#62C462, #51A351);
}
&.padded {
margin-right:3px;
padding:4px 10px 4px;
.bar-danger {
background-color: #B94A48;
@include bg-gradient(#EE5F5B, #BD362F);
}
}
.upvotes {
display: inline-block;
color: #468847;
}
.downvotes {
display: inline-block;
color: #B94A48;
}
}
.prettyprint {
background-color: #fefbf3;
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-block {
margin: 14px 6px 6px 0;
.downvotes {
float: right;
}
}
.upvotes {
font-size: 14px;
font-weight: bold;
color: #468847;
text-align: right;
padding: 4px;
margin: 2px;
.votes-inline {
display: inline-block;
margin: 0 8px;
.progress {
display: inline-block;
padding: 0 0 2px;
width: 45px;
}
}
/* Fix for readme code (stopped it from being yellow) */
......@@ -549,14 +598,6 @@ li.note {
}
/**
* Milestones list
*
*/
.milestone {
@extend .wll;
}
/**
* Admin area
......@@ -603,11 +644,10 @@ li.note {
*
*/
.event_lp {
@extend .alert-info;
@extend .ui-box;
color:#777;
margin-bottom:20px;
padding:8px;
border-style: solid;
border-width: 1px;
@include border-radius(4px);
min-height:22px;
......@@ -621,88 +661,19 @@ li.note {
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,
.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{
background: #EFE;
border-color:#CEC;
.icon {
background: #ADA;
border:1px solid #8B8;
}
}
&.closed {
background: #F5f5f5;
border-color:#E5E5E5;
.icon {
background: #DDD;
border:1px solid #BBB;
}
}
&.yours {
.icon {
background: #AAD;
border:1px solid #88B;
}
}
&.merged {
background: #F5f5f5;
border-color:#E5E5E5;
.icon {
background: #DAD;
border:1px solid #B8B;
}
}
}
......@@ -714,7 +685,7 @@ li.note {
margin-right:40px;
.prev {
@extend .borders;
@extend .thumbnail;
height:120px;
width:175px;
margin-bottom:10px;
......@@ -743,3 +714,31 @@ li.note {
text-align:center;
margin-bottom:10px;
}
.oauth_select_holder {
padding:20px;
img {
padding:5px;
margin-right:10px;
}
.active {
img {
border:1px solid #ccc;
background:$hover;
@include border-radius(5px);
}
}
}
.btn-build-token {
float: left;
padding: 6px 20px;
margin-right: 12px;
}
.gitlab-promo {
a {
color:#aaa;
margin-right: 30px;
}
}
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;
padding:0px;
font-size:12px;
table-layout:fixed;
background: #EEE;
box-shadow: none;
border: none;
td.linenos {
background:#eee;
border-left:none;
}
td.code {
border-right:none;
}
}
td.code,
td.linenos{
padding:0;
......
@import "bootstrap";
@import "bootstrap-responsive";
/** GITLAB colors **/
$text_color:#222;
$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";
/** GitLab colors **/
$link_color:#3A89A3;
$blue_link: #2fa0bb;
$style_color: #474d57;
$hover: #fdf5d9;
/** Style colors **/
$style_color: #474D57;
$hover: #FDF5D9;
/** GITLAB Fonts **/
/** GitLab Fonts **/
@font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); }
/** MIXINS **/
......@@ -72,7 +56,27 @@ $hover: #FDF5D9;
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.
......@@ -109,11 +113,17 @@ $hover: #FDF5D9;
@import "themes/ui_modern.scss";
/**
* Gitlab bootstrap.
* GitLab 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;
*/
@import "common.scss";
/**
* Styles related to specific part of app
*/
......@@ -151,6 +160,11 @@ $hover: #FDF5D9;
*/
@import "sections/notes.scss";
/**
* This file represent profile styles
*/
@import "sections/profile.scss";
/**
* 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 @@
.chzn-drop {
margin:7px 0;
border: 1px solid #CCC;
min-width: 300px;
min-width: 400px;
border: 2px solid $blue_link;
@include border-radius(4px);
.chzn-results {
max-height:300px;
.group-result {
color: $blue_link;
}
.active-result {
&.highlighted {
background: $blue_link;
}
}
}
.chzn-search input {
min-width:200px;
min-width:365px;
}
}
.chzn-single {
background:#ddd;
//border:none;
//box-shadow:none;
@include bg-gray-gradient;
div {
background:transparent;
......
......@@ -206,4 +206,24 @@
min-width:65px;
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 @@
h4 {
padding:0 10px;
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);
@include bg-gray-gradient;
}
.graph {
......
......@@ -22,7 +22,7 @@ header {
*
*/
.app_logo {
width:230px;
width:200px;
float:left;
position:relative;
top:-5px;
......@@ -31,7 +31,7 @@ header {
h1 {
padding-top: 5px;
width:102px;
width:90px;
background: url('logo_dark.png') no-repeat 0px -3px;
float:left;
margin-left:5px;
......
......@@ -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_filters {
form {
......@@ -99,3 +104,12 @@ input.check_all_issues {
#update_status {
width:100px;
}
/**
* Milestones list
*
*/
.milestone {
@extend .wll;
}
......@@ -11,23 +11,6 @@
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 @@
background: #CEB;
.accept_merge_request {
font-size:13px;
float:left;
}
.remove_branch_holder {
......@@ -99,3 +83,41 @@ li.merge_request {
@extend .padded;
@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 {
border-radius: 4px;
margin: auto;
margin:30px 0;
background:#eee;
border:1px solid #bbb;
border:1px solid #AAA;
height:37px;
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 bg-gray-gradient;
position:relative;
overflow:hidden;
@include shade;
......@@ -59,7 +55,6 @@ ul.main_menu {
&.current {
background-color:#D5D5D5;
border-bottom: 2px solid $style_color;
border-right: 1px solid #BBB;
border-left: 1px solid #BBB;
border-radius: 0 0 1px 1px;
......@@ -89,7 +84,7 @@ ul.main_menu {
line-height:36px;
color: $style_color;
text-shadow:0 1px 1px white;
padding:0 10px;
}
}
/*
......
......@@ -3,17 +3,13 @@
*
*/
#notes-list,
#new_notes_list {
#new-notes-list {
display:block;
list-style:none;
margin:0px;
padding:0px;
}
#new_notes_list li:last-child{
border-bottom:1px solid #aaa;
}
.issue_notes,
.wiki_notes {
.note_content {
......@@ -30,17 +26,21 @@
}
#new_note {
#note_note {
height:25px;
}
.attach_holder {
display:none;
}
}
.preview_note {
margin: 2px;
border: 1px solid #ddd;
padding: 10px;
min-height: 60px;
background:#f5f5f5;
}
.note {
padding: 8px 0;
border-bottom: 1px solid #eee;
overflow: hidden;
display: block;
img {float: left; margin-right: 10px;}
......@@ -62,6 +62,23 @@
.delete-note { display:block; }
}
}
#notes-list:not(.reversed) .note,
#new-notes-list:not(.reversed) .note {
border-bottom: 1px solid #eee;
}
#notes-list.reversed .note,
#new-notes-list.reversed .note {
border-top: 1px solid #eee;
}
/* mark vote notes */
.voting_notes .note {
padding: 8px 0;
}
.notes-status {
margin: 18px;
}
p.notify_controls input{
......@@ -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 @@
text-shadow: 0 1px 1px #fff;
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 .ui-box;
}
......@@ -33,9 +59,38 @@
color:#888;
}
.btn {
padding:6px;
padding:6px 10px;
margin-left:10px;
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 @@
@include border-radius(0);
.tree-item {
&:hover {
td { background: $hover; }
td {
background: $hover;
border-top:1px solid #FEA;
border-bottom:1px solid #FEA;
}
cursor:pointer;
}
}
......@@ -53,31 +57,9 @@
#tree-slider {
@include solid_shade;
width:100%;
border-color:#ccc;
td {
padding:8px;
border-color:#f1f1f1;
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 {
......
class MergeRequestsLoad < BaseContext
def execute
type = params[:f].to_i
type = params[:f]
merge_requests = project.merge_requests
merge_requests = case type
when 1 then merge_requests
when 2 then merge_requests.closed
when 3 then merge_requests.opened.assigned(current_user)
when 'all' then merge_requests
when 'closed' then merge_requests.closed
when 'assigned-to-me' then merge_requests.opened.assigned(current_user)
else merge_requests.opened
end.page(params[:page]).per(20)
......
......@@ -3,29 +3,30 @@ module Notes
def execute
target_type = params[:target_type]
target_id = params[:target_id]
first_id = params[:first_id]
last_id = params[:last_id]
after_id = params[:after_id]
before_id = params[:before_id]
@notes = case target_type
when "commit"
then project.commit_notes(project.commit(target_id)).fresh.limit(20)
when "snippet"
then project.snippets.find(target_id).notes
when "wall"
then project.common_notes.order("created_at DESC").fresh.limit(50)
project.commit_notes(project.commit(target_id)).fresh.limit(20)
when "issue"
then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
project.issues.find(target_id).notes.inc_author.fresh.limit(20)
when "merge_request"
then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20)
when "snippet"
project.snippets.find(target_id).notes.fresh
when "wall"
# this is the only case, where the order is DESC
project.common_notes.order("created_at DESC, id DESC").limit(50)
when "wiki"
then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
project.wiki_notes.limit(20)
end
@notes = if last_id
@notes.where("id > ?", last_id)
elsif first_id
@notes.where("id < ?", first_id)
@notes = if after_id
@notes.where("id > ?", after_id)
elsif before_id
@notes.where("id < ?", before_id)
else
@notes
end
......
class Admin::DashboardController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::DashboardController < AdminController
def index
@workers = Resque.workers
@pending_jobs = Resque.size(:post_receive)
......
class Admin::HooksController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::HooksController < AdminController
def index
@hooks = SystemHook.all
@hook = SystemHook.new
......
class Admin::LogsController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::LogsController < AdminController
end
class Admin::ProjectsController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::ProjectsController < AdminController
before_filter :admin_project, only: [:edit, :show, :update, :destroy, :team_update]
def index
......
class Admin::ResqueController < ApplicationController
layout 'admin'
class Admin::ResqueController < AdminController
def show
end
end
class Admin::TeamMembersController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::TeamMembersController < AdminController
def edit
@admin_team_member = UsersProject.find(params[:id])
end
......
class Admin::UsersController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
class Admin::UsersController < AdminController
def index
@admin_users = User.scoped
@admin_users = @admin_users.filter(params[:filter])
......
# 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
helper_method :abilities, :can?
rescue_from Gitlab::Gitolite::AccessDenied do |exception|
render "errors/gitolite", layout: "error"
render "errors/gitolite", layout: "error", status: 500
end
rescue_from Encoding::CompatibilityError do |exception|
render "errors/encoding", layout: "error", status: 404
render "errors/encoding", layout: "error", status: 500
end
rescue_from ActiveRecord::RecordNotFound do |exception|
......@@ -84,10 +84,6 @@ class ApplicationController < ActionController::Base
abilities << Ability
end
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_project!(action)
return access_denied! unless can?(current_user, action, project)
end
......@@ -116,22 +112,12 @@ class ApplicationController < ActionController::Base
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
render file: File.join(Rails.root, "public", "404"), layout: false, status: "404"
end
def require_non_empty_project
redirect_to @project unless @project.repo_exists? && @project.has_commits?
redirect_to @project if @project.empty_repo?
end
def no_cache_headers
......
......@@ -52,6 +52,7 @@ class CommitsController < ApplicationController
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = []
@commits = CommitDecorator.decorate(@commits)
......@@ -64,7 +65,14 @@ class CommitsController < ApplicationController
@commit.to_patch,
type: "text/plain",
disposition: 'attachment',
filename: (@commit.id.to_s + ".patch")
filename: "#{@commit.id}.patch"
)
end
protected
def load_refs
@ref ||= params[:ref].presence || params[:branch].presence || params[:tag].presence
@ref ||= @ref || @project.try(:default_branch) || 'master'
end
end
......@@ -17,7 +17,7 @@ class IssuesController < ApplicationController
before_filter :authorize_write_issue!, only: [:new, :create]
# Allow modify issue
before_filter :authorize_modify_issue!, only: [:close, :edit, :update]
before_filter :authorize_modify_issue!, only: [:edit, :update]
# Allow destroy issue
before_filter :authorize_admin_issue!, only: [:destroy]
......@@ -37,7 +37,7 @@ class IssuesController < ApplicationController
end
def new
@issue = @project.issues.new
@issue = @project.issues.new(params[:issue])
respond_with(@issue)
end
......@@ -60,7 +60,13 @@ class IssuesController < ApplicationController
@issue.save
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
end
end
......@@ -81,8 +87,6 @@ class IssuesController < ApplicationController
end
def destroy
return access_denied! unless can?(current_user, :admin_issue, @issue)
@issue.destroy
respond_to do |format|
......@@ -162,10 +166,10 @@ class IssuesController < ApplicationController
def issues_filter
{
all: "1",
closed: "2",
to_me: "3",
open: "0"
all: "all",
closed: "closed",
to_me: "assigned-to-me",
open: "open"
}
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
def branch_from
@commit = project.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end
def branch_to
@commit = project.commit(params[:ref])
@commit = CommitDecorator.decorate(@commit)
end
protected
......
......@@ -17,8 +17,8 @@ class MilestonesController < ApplicationController
respond_to :html
def index
@milestones = case params[:f].to_i
when 1; @project.milestones
@milestones = case params[:f]
when 'all'; @project.milestones
else @project.milestones.active
end
......
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
Gitlab.config.omniauth_providers.each do |provider|
define_method provider['name'] do
handle_omniauth
end
end
# Extend the standard message generation to accept our custom exception
def failure_message
......@@ -19,4 +24,27 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
sign_in_and_redirect @user
end
private
def handle_omniauth
oauth = request.env['omniauth.auth']
provider, uid = oauth['provider'], oauth['uid']
if current_user
# Change a logged-in user's authentication method:
current_user.extern_uid = uid
current_user.provider = provider
current_user.save
redirect_to profile_path
else
@user = User.find_or_new_for_omniauth(oauth)
if @user
sign_in_and_redirect @user
else
flash[:notice] = "There's no such user!"
redirect_to new_user_session_path
end
end
end
end
......@@ -16,9 +16,6 @@ class ProfileController < ApplicationController
def token
end
def password
end
def password_update
params[:user].reject!{ |k, v| k != "password" && k != "password_confirmation"}
......@@ -32,7 +29,11 @@ class ProfileController < ApplicationController
def reset_private_token
current_user.reset_authentication_token!
redirect_to profile_token_path
redirect_to profile_account_path
end
def history
@events = current_user.recent_events.page(params[:page]).per(20)
end
private
......
......@@ -50,7 +50,7 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html do
if @project.repo_exists? && @project.has_commits?
unless @project.empty_repo?
@last_push = current_user.recent_push(@project.id)
render :show
else
......
require 'github/markup'
class RefsController < ApplicationController
include Gitlab::Encode
before_filter :project
......
......@@ -5,7 +5,10 @@ class TeamMembersController < ApplicationController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_admin_project!, except: [:show]
before_filter :authorize_admin_project!, except: [:index, :show]
def index
end
def show
@team_member = project.users_projects.find(params[:id])
......@@ -17,13 +20,12 @@ class TeamMembersController < ApplicationController
end
def create
@team_member = UsersProject.new(params[:team_member])
@team_member.project = project
if @team_member.save
redirect_to team_project_path(@project)
else
render "new"
end
@project.add_users_ids_to_team(
params[:user_ids],
params[:project_access]
)
redirect_to project_team_index_path(@project)
end
def update
......@@ -33,7 +35,7 @@ class TeamMembersController < ApplicationController
unless @team_member.valid?
flash[:alert] = "User should have at least one role"
end
redirect_to team_project_path(@project)
redirect_to project_team_index_path(@project)
end
def destroy
......@@ -41,7 +43,7 @@ class TeamMembersController < ApplicationController
@team_member.destroy
respond_to do |format|
format.html { redirect_to team_project_path(@project) }
format.html { redirect_to project_team_index_path(@project) }
format.js { render nothing: true }
end
end
......
class CommitDecorator < ApplicationDecorator
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.
#
# 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
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
return no_commit_message if safe_message.blank?
title_end = safe_message.index(/\n/)
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
safe_message[0..69] << "&hellip;".html_safe
......@@ -26,4 +37,10 @@ class CommitDecorator < ApplicationDecorator
safe_message.split(/\n/, 2)[1].try(:chomp)
end
end
protected
def no_commit_message
"--no commit message"
end
end
......@@ -8,6 +8,8 @@ class EventDecorator < ApplicationDecorator
"#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title
elsif self.push?
"#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name
elsif self.membership_changed?
"#{self.author_name} #{self.action_name} #{self.project.name}"
else
""
end
......
......@@ -62,7 +62,7 @@ module ApplicationHelper
{ label: "#{@project.name} / Wall", url: wall_project_path(@project) },
{ label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) },
{ label: "#{@project.name} / Commits", url: project_commits_path(@project) },
{ label: "#{@project.name} / Team", url: team_project_path(@project) }
{ label: "#{@project.name} / Team", url: project_team_index_path(@project) }
]
end
......@@ -104,7 +104,8 @@ module ApplicationHelper
# Profile Area
when :profile; current_page?(controller: "profile", action: :show)
when :password; current_page?(controller: "profile", action: :password)
when :history; current_page?(controller: "profile", action: :history)
when :account; current_page?(controller: "profile", action: :account)
when :token; current_page?(controller: "profile", action: :token)
when :design; current_page?(controller: "profile", action: :design)
when :ssh_keys; controller.controller_name == "keys"
......@@ -126,4 +127,19 @@ module ApplicationHelper
def hexdigest(string)
Digest::SHA1.hexdigest string
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
module GitlabMarkdownHelper
# Replaces references (i.e. @abc, #123, !456, ...) in the text with links to
# 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
include Gitlab::Markdown
# Use this in places where you would normally use link_to(gfm(...), ...).
#
......@@ -44,7 +11,9 @@ module GitlabMarkdownHelper
# explicitly produce the correct linking behavior (i.e.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
def link_to_gfm(body, url, html_options = {})
gfm_body = gfm(body, html_options)
return "" if body.blank?
gfm_body = gfm(escape_once(body), html_options)
gfm_body.gsub!(%r{<a.*?>.*?</a>}m) do |match|
"</a>#{match}#{link_to("", url, html_options)[0..-5]}" # "</a>".length +1
......@@ -54,7 +23,14 @@ module GitlabMarkdownHelper
end
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,
tables: true,
fenced_code_blocks: true,
......@@ -62,9 +38,9 @@ module GitlabMarkdownHelper
strikethrough: true,
lax_html_blocks: true,
space_after_headers: true,
superscript: true
})
superscript: true)
end
@__renderer.render(text).html_safe
@markdown.render(text).html_safe
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
end
def project_tab_class
[:show, :files, :team, :edit, :update].each do |action|
[:show, :files, :edit, :update].each do |action|
return "current" if current_page?(controller: "projects", action: action, id: @project)
end
......
......@@ -18,10 +18,25 @@ module TreeHelper
end
def tree_full_path(content)
content.name.force_encoding('utf-8')
if params[:path]
File.join(params[:path], content.name)
else
content.name
end
end
# Public: Determines if a given filename is compatible with GitHub::Markup.
#
# filename - Filename string to check
#
# Returns boolean
def markup?(filename)
filename.end_with?(*%w(.textile .rdoc .org .creole
.mediawiki .rst .asciidoc .pod))
end
def gitlab_markdown?(filename)
filename.end_with?(*%w(.mdown .md .markdown))
end
end
......@@ -76,6 +76,21 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
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
# Look up a User by their ID and return their email address
......@@ -96,18 +111,18 @@ class Notify < ActionMailer::Base
# Examples
#
# >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum"
# => "GitLab | Lorem ipsum"
#
# # Automatically inserts Project name when @project is set
# >> @project = Project.last
# => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
# >> subject('Lorem ipsum')
# => "gitlab | Lorem ipsum | Ruby on Rails"
# => "GitLab | Lorem ipsum | Ruby on Rails"
#
# # Accepts multiple arguments
# >> subject('Lorem ipsum', 'Dolor sit amet')
# => "gitlab | Lorem ipsum | Dolor sit amet"
# => "GitLab | Lorem ipsum | Dolor sit amet"
def subject(*extra)
"gitlab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
"GitLab | " << extra.join(' | ') << (@project ? " | #{@project.name}" : "")
end
end
class Commit
include ActiveModel::Conversion
include Gitlab::Encode
include StaticModel
extend ActiveModel::Naming
attr_accessor :commit
......@@ -22,7 +23,6 @@ class Commit
:to_patch,
to: :commit
class << self
def find_or_first(repo, commit_id = nil, root_ref)
commit = if commit_id
......@@ -82,20 +82,24 @@ class Commit
end
def compare(project, from, to)
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
result = {
commits: [],
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
commits = [first, last].sort_by(&:created_at)
younger = commits.first
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[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older)
......@@ -105,10 +109,6 @@ class Commit
end
end
def persisted?
false
end
def initialize(raw_commit, head = nil)
@commit = raw_commit
@head = head
......
......@@ -10,6 +10,8 @@ class Event < ActiveRecord::Base
Pushed = 5
Commented = 6
Merged = 7
Joined = 8 # User joined project
Left = 9 # User left project
belongs_to :project
belongs_to :target, polymorphic: true
......@@ -37,7 +39,15 @@ class Event < ActiveRecord::Base
# - new issue
# - merge request
def allowed?
push? || issue? || merge_request?
push? || issue? || merge_request? || membership_changed?
end
def project_name
if project
project.name
else
"(deleted)"
end
end
def push?
......@@ -84,6 +94,18 @@ class Event < ActiveRecord::Base
[Closed, Reopened].include?(action)
end
def joined?
action == Joined
end
def left?
action == Left
end
def membership_changed?
joined? || left?
end
def issue
target if target_type == "Issue"
end
......@@ -101,6 +123,10 @@ class Event < ActiveRecord::Base
"closed"
elsif merged?
"merged"
elsif joined?
'joined'
elsif left?
'left'
else
"opened"
end
......
class Issue < ActiveRecord::Base
include IssueCommonality
include Upvote
include Votes
acts_as_taggable_on :labels
......
require 'digest/md5'
class Key < ActiveRecord::Base
include SshKey
belongs_to :user
belongs_to :project
attr_protected :user_id
validates :title,
presence: true,
length: { within: 0..255 }
validates :key,
presence: true,
format: { :with => /ssh-.{3} / },
length: { within: 0..5000 }
before_save :set_identifier
......@@ -50,6 +52,10 @@ class Key < ActiveRecord::Base
user.projects
end
end
def last_deploy?
Key.where(identifier: identifier).count == 0
end
end
# == Schema Information
#
......
......@@ -2,7 +2,7 @@ require File.join(Rails.root, "app/models/commit")
class MergeRequest < ActiveRecord::Base
include IssueCommonality
include Upvote
include Votes
BROKEN_DIFF = "--broken-diff"
......@@ -162,7 +162,7 @@ class MergeRequest < ActiveRecord::Base
end
def automerge!(current_user)
if Gitlab::Merge.new(self, current_user).merge
if Gitlab::Merge.new(self, current_user).merge && self.unmerged_commits.empty?
self.merge!(current_user.id)
true
end
......
......@@ -28,18 +28,10 @@ class Milestone < ActiveRecord::Base
end
def percent_complete
@percent_complete ||= begin
total_i = self.issues.count
closed_i = self.issues.closed.count
if total_i > 0
(closed_i * 100) / total_i
else
((self.issues.closed.count * 100) / self.issues.count).abs
rescue ZeroDivisionError
100
end
rescue => ex
0
end
end
def expires_at
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
......
......@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base
scope :today, where("created_at >= :date", date: Date.today)
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
scope :since, lambda { |day| where("created_at >= :date", date: (day)) }
scope :fresh, order("created_at DESC")
scope :fresh, order("created_at ASC, id ASC")
scope :inc_author_project, includes(:project, :author)
scope :inc_author, includes(:author)
......@@ -103,7 +103,13 @@ class Note < ActiveRecord::Base
# Returns true if this is an upvote note,
# otherwise false is returned
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
# == Schema Information
......
......@@ -104,6 +104,8 @@ class Project < ActiveRecord::Base
length: { within: 1..255 }
validates :owner, presence: true
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validate :check_limit
validate :repo_name
......@@ -158,7 +160,7 @@ class Project < ActiveRecord::Base
end
def last_activity
events.last || nil
events.order("created_at ASC").last
end
def last_activity_date
......@@ -169,6 +171,10 @@ class Project < ActiveRecord::Base
end
end
def wiki_notes
Note.where(noteable_id: wikis.map(&:id), noteable_type: 'Wiki', project_id: self.id)
end
def project_id
self.id
end
......@@ -187,7 +193,7 @@ end
# private_flag :boolean(1) default(TRUE), not null
# code :string(255)
# owner_id :integer(4)
# default_branch :string(255) default("master"), not null
# default_branch :string(255)
# issues_enabled :boolean(1) default(TRUE), not null
# wall_enabled :boolean(1) default(TRUE), not null
# merge_requests_enabled :boolean(1) default(TRUE), not null
......
class ProtectedBranch < ActiveRecord::Base
include GitHost
belongs_to :project
validates_presence_of :project_id
validates_presence_of :name
......@@ -7,7 +9,7 @@ class ProtectedBranch < ActiveRecord::Base
after_destroy :update_repository
def update_repository
Gitlab::GitHost.system.update_project(project.path, project)
git_host.update_repository(project)
end
def commit
......
......@@ -16,7 +16,7 @@ class Tree
def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path,
@tree = if path
raw_tree / path
raw_tree / path.dup.force_encoding('ascii-8bit')
else
raw_tree
end
......
......@@ -86,33 +86,20 @@ class User < ActiveRecord::Base
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end
def self.find_for_ldap_auth(auth, signed_in_resource=nil)
uid = auth.info.uid
provider = auth.provider
name = auth.info.name.force_encoding("utf-8")
email = auth.info.email.downcase unless auth.info.email.nil?
raise OmniAuth::Error, "LDAP accounts must provide an uid and email address" if uid.nil? or email.nil?
if @user = User.find_by_extern_uid_and_provider(uid, provider)
@user
# workaround for backward compatibility
elsif @user = User.find_by_email(email)
logger.info "Updating legacy LDAP user #{email} with extern_uid => #{uid}"
@user.update_attributes(:extern_uid => uid, :provider => provider)
@user
else
logger.info "Creating user from LDAP login {uid => #{uid}, name => #{name}, email => #{email}}"
password = Devise.friendly_token[0, 8].downcase
@user = User.create(
:extern_uid => uid,
:provider => provider,
:name => name,
:email => email,
:password => password,
:password_confirmation => password,
:projects_limit => Gitlab.config.default_projects_limit
)
def self.create_from_omniauth(auth, ldap = false)
gitlab_auth.create_from_omniauth(auth, ldap)
end
def self.find_or_new_for_omniauth(auth)
gitlab_auth.find_or_new_for_omniauth(auth)
end
def self.find_for_ldap_auth(auth, signed_in_resource = nil)
gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
end
def self.gitlab_auth
Gitlab::Auth.new
end
def self.search query
......@@ -148,4 +135,3 @@ end
# bio :string(255)
# blocked :boolean(1) default(FALSE), not null
#
class UsersProject < ActiveRecord::Base
include GitHost
GUEST = 10
REPORTER = 20
DEVELOPER = 30
......@@ -12,12 +14,29 @@ class UsersProject < ActiveRecord::Base
after_save :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 :project_id
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)
UsersProject.transaction do
user_ids.each do |user_id|
......@@ -58,9 +77,7 @@ class UsersProject < ActiveRecord::Base
end
def update_repository
Gitlab::GitHost.system.new.configure do |c|
c.update_project(project.path, project)
end
git_host.update_repository(project)
end
def project_access_human
......
......@@ -28,7 +28,6 @@ class Wiki < ActiveRecord::Base
end
new_wiki
end
end
end
# == Schema Information
......
......@@ -9,8 +9,16 @@ class IssueObserver < ActiveRecord::Observer
def after_update(issue)
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
protected
......
class KeyObserver < ActiveRecord::Observer
include GitHost
def after_save(key)
key.update_repository
git_host.set_key(key.identifier, key.key, key.projects)
end
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
......@@ -4,6 +4,18 @@ class ProjectObserver < ActiveRecord::Observer
end
def after_destroy(project)
log_info("Project \"#{project.name}\" was removed")
project.destroy_repository
end
def after_create project
log_info("#{project.owner.name} created a new project \"#{project.name}\"")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end
class UserObserver < ActiveRecord::Observer
def after_create(user)
log_info("User \"#{user.name}\" (#{user.email}) was created")
Notify.new_user_email(user.id, user.password).deliver
end
def after_destroy user
log_info("User \"#{user.name}\" (#{user.email}) was removed")
end
protected
def log_info message
Gitlab::AppLogger.info message
end
end
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
# 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
email.gsub /[^[:alnum:]]/, "_"
# Replace non-word chars with underscores, then make sure it starts with
# valid chars
email.gsub(/\W/, '_').gsub(/\A([\W\_])+/, '')
end
def is_admin?
......
module GitHost
def git_host
Gitlab::Gitolite.new
end
end
......@@ -16,7 +16,7 @@ module IssueCommonality
validates :title,
presence: true,
length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] }
scope :opened, where(closed: false)
scope :closed, where(closed: true)
......
......@@ -90,6 +90,8 @@ module PushEvent
def push_with_commits?
md_ref? && commits.any? && parent_commit && last_commit
rescue Grit::NoSuchPathError
false
end
def last_push_to_non_root?
......
# Includes methods for handling Git Push events
#
# Triggered by PostReceive job
module PushObserver
def observe_push(oldrev, newrev, ref, user)
data = post_receive_data(oldrev, newrev, ref, user)
......@@ -84,11 +87,10 @@ module PushObserver
data
end
# This method will be called after each post receive
# and only if user present in gitlab.
# All callbacks for post receive should be placed here
# This method will be called after each post receive and only if the provided
# user is present in GitLab.
#
# All callbacks for post receive should be placed here.
def trigger_post_receive(oldrev, newrev, ref, user)
# Create push event
self.observe_push(oldrev, newrev, ref, user)
......@@ -101,5 +103,11 @@ module PushObserver
# Create satellite
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
module Repository
include GitHost
def valid_repo?
repo
rescue
......@@ -6,6 +8,10 @@ module Repository
false
end
def empty_repo?
!repo_exists? || !has_commits?
end
def commit(commit_id = nil)
Commit.find_or_first(repo, commit_id, root_ref)
end
......@@ -48,7 +54,7 @@ module Repository
end
def url_to_repo
Gitlab::GitHost.url_to_repo(path)
git_host.url_to_repo(path)
end
def path_to_repo
......@@ -56,11 +62,11 @@ module Repository
end
def update_repository
Gitlab::GitHost.system.update_project(path, self)
git_host.update_repository(self)
end
def destroy_repository
Gitlab::GitHost.system.destroy_project(self)
git_host.remove_repository(self)
end
def repo_exists?
......@@ -73,6 +79,14 @@ module Repository
@heads ||= repo.heads
end
def branches_names
heads.map(&:name)
end
def ref_names
[branches_names + tags].flatten
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
......@@ -88,6 +102,24 @@ module Repository
end.sort_by(&:name)
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?
!!commit
end
......@@ -96,7 +128,7 @@ module Repository
default_branch || "master"
end
def root_ref? branch
def root_ref?(branch)
root_ref == branch
end
......@@ -105,7 +137,7 @@ module Repository
# Already packed repo archives stored at
# 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
commit = self.commit(ref)
return nil unless commit
......@@ -115,10 +147,13 @@ module Repository
storage_path = File.join(Rails.root, "tmp", "repositories", self.code)
file_path = File.join(storage_path, file_name)
# Put files into a directory before archiving
prefix = self.code + "/"
# Create file if not exists
unless File.exists?(file_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
file_path
......@@ -129,6 +164,6 @@ module Repository
end
def http_url_to_repo
http_url = [Gitlab.config.url, "/", path, ".git"].join()
http_url = [Gitlab.config.url, "/", path, ".git"].join('')
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
UsersProject.bulk_import(self, users_ids, access_role)
self.update_repository
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
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 @@
%h3 Latest projects
%hr
- @projects.each do |project|
%h5
%p
= link_to project.name, [:admin, project]
.span6
%h3 Latest users
%hr
- @users.each do |user|
%h5
= link_to user.name, [:admin, user]
%p
= link_to [:admin, user] do
= user.name
%small= user.email
......@@ -23,7 +23,7 @@
Hooks
%small (#{@hooks.count})
%br
%table.admin-table
%table
%tr
%th URL
%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
%i.icon-file
githost.log
.file_content.logs
%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
%p= line
......@@ -10,19 +10,17 @@
Project name is
.input
= f.text_field :name, placeholder: "Example Project", class: "xxlarge"
= f.submit project.new_record? ? 'Create project' : 'Save Project', class: "btn primary"
%hr
.alert.alert-info
%h5 Advanced settings:
.adv_settings
%h6 Advanced settings:
.clearfix
= f.label :path do
Git Clone
Path
.input
.input-prepend
%span.add-on= Gitlab.config.ssh_path
= f.text_field :path, placeholder: "example_project", disabled: !!project.id
%span.add-on= ".git"
%strong
= text_field_tag :ppath, @admin_project.path_to_repo, class: "xlarge", disabled: true
.clearfix
= f.label :code do
URL
......@@ -34,7 +32,7 @@
- unless project.new_record?
.clearfix
= f.label :owner_id
.input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }
.input= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
- if project.repo_exists?
.clearfix
......@@ -42,8 +40,9 @@
.input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;")
- unless project.new_record?
.alert.alert-info
%h5 Features:
%hr
.adv_settings
%h6 Features:
.clearfix
= f.label :issues_enabled, "Issues"
......@@ -63,13 +62,13 @@
- unless project.new_record?
.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
$(function(){
$('#project_owner_id').chosen();
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
= link_to 'New Project', new_admin_project_path, class: "btn small right"
%br
......@@ -6,7 +6,7 @@
= text_field_tag :name, params[:name], class: "xlarge"
= submit_tag "Search", class: "btn submit primary"
%table.admin-table
%table
%thead
%th Name
%th Path
......
%h3.page_title New project
%hr
= render 'form', project: @admin_project
.project_new_holder
%h3.page_title
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 @@
= link_to 'Edit', edit_admin_project_path(@admin_project), class: "btn right small"
%br
%table.zebra-striped.table-bordered
%table.zebra-striped
%thead
%tr
%th Project
%th
%tr
%td
%b
......@@ -40,7 +44,7 @@
%small
(#{@admin_project.users_projects.count})
%br
%table.zebra-striped.table-bordered
%table.zebra-striped
%thead
%tr
%th Name
......@@ -60,32 +64,18 @@
%h3 Add new team member
%br
= form_tag team_update_admin_project_path(@admin_project), class: "bulk_import", method: :put do
%table.zebra-striped.table-bordered
%table.zebra-striped
%thead
%tr
%th Users
%th Project Access:
%tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select"
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
%tr
%td= submit_tag 'Add', class: "btn primary"
%td
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
:css
form select {
width:150px;
}
#user_ids {
width:300px;
}
:javascript
$('select#user_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();
%h3 Resque
%iframe{src: "/info/resque", width: 1168, height: 600, style: "border: none"}
\ No newline at end of file
%h3.page_title Resque
%br
.ui-box
%iframe{src: resque_url, width: '100%', height: 600, style: "border: none"}
......@@ -8,20 +8,9 @@
.clearfix
%label Project Access:
.input
= f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select"
= f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
%br
.actions
= f.submit 'Save', class: "btn primary"
= link_to 'Cancel', :back, class: "btn"
:css
form select {
width:300px;
}
:javascript
$('select#team_member_user_id').chosen();
$('select#team_member_project_id').chosen();
$('select#team_member_repo_access').chosen();
$('select#team_member_project_access').chosen();
......@@ -2,12 +2,14 @@
= form_for [:admin, @admin_user] do |f|
-if @admin_user.errors.any?
#error_explanation
%ul
%ul.unstyled.alert.alert-error
- @admin_user.errors.full_messages.each do |msg|
%li= msg
.row
.span6
.span7
.ui-box
%br
.clearfix
= f.label :name
.input
......@@ -19,12 +21,11 @@
= f.text_field :email
%span.help-inline * required
%hr
-if f.object.new_record?
.clearfix
= f.label :admin, class: "checkbox" do
= f.check_box :force_random_password, {}, true, nil
= f.label :force_random_password do
%span Generate random password
.input= f.check_box :force_random_password, {}, true, nil
%div.password-fields
.clearfix
......@@ -43,30 +44,37 @@
.clearfix
= f.label :twitter
.input= f.text_field :twitter
.span6
.span5
.ui-box
%br
.clearfix
= f.label :projects_limit
.input= f.text_field :projects_limit, class: "small_input"
.input= f.number_field :projects_limit
.alert
.clearfix
%p Make the user a GitLab administrator.
= f.label :admin, class: "checkbox" do
= f.check_box :admin
%span Administrator
= f.label :admin do
%strong.cred Administrator
.input= f.check_box :admin
- unless @admin_user.new_record?
.alert.alert-error
%hr
.padded.cred
- if @admin_user.blocked
%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
.clearfix
= link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn small right"
- else
%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.
.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
= f.submit 'Save', class: "btn primary"
= f.submit 'Save', class: "btn save-btn"
- if @admin_user.new_record?
= link_to 'Cancel', admin_users_path, class: "btn"
= link_to 'Cancel', admin_users_path, class: "btn cancel-btn"
- 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
= render 'form'
%h3
%h3.page_title
Users
= link_to 'New User', new_admin_user_path, class: "btn small right"
%br
......@@ -19,7 +19,7 @@
= link_to admin_users_path(filter: "wop") do
Without projects
%table.admin-table
%table
%thead
%th Admin
%th Name
......
%h2 New user
%hr
%h3.page_title New user
%br
= render 'form'
......@@ -8,7 +8,11 @@
%br
%table.zebra-striped.table-bordered
%table.zebra-striped
%thead
%tr
%th Profile
%th
%tr
%td
%b
......@@ -57,15 +61,15 @@
%h3 Add User to Projects
%br
= form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do
%table.table-bordered
%table
%thead
%tr
%th Projects
%th Project Access:
%tr
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select"
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
%td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
%tr
%td= submit_tag 'Add', class: "btn primary"
......@@ -78,9 +82,9 @@
%h3 Projects
%br
%table.zebra-striped.table-bordered
%tr
%table.zebra-striped
%thead
%tr
%th Name
%th Project Access
%th
......@@ -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= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
:css
form select {
width:150px;
}
#project_ids {
width:300px;
}
:javascript
$('select#project_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();
......@@ -4,8 +4,8 @@
%strong= link_to "Browse Code »", tree_project_ref_path(@project, commit.id), class: "right"
%p
= link_to commit.short_id(8), project_commit_path(@project, id: commit.id), class: "commit_short_id"
%strong.cgray= commit.author_name
&ndash;
%strong.commit-author-name= commit.author_name
%span.dash &ndash;
= 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"
......
......@@ -11,10 +11,10 @@
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do
%strong Browse Code »
%h3.commit-title.page_title
= gfm @commit.title
= gfm escape_once(@commit.title)
- if @commit.description.present?
%pre.commit-description
= gfm @commit.description
= gfm escape_once(@commit.description)
.commit-info
.row
.span4
......
%ul.nav.nav-tabs
%li
= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
= select_tag "ref", grouped_options_refs, onchange: "$(this.form).trigger('submit');", class: "project-refs-select"
= hidden_field_tag :destination, "commits"
%li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
= link_to project_commits_path(@project) do
Commits
......@@ -20,14 +16,8 @@
Tags
%span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token
%li.right
%span.rss-icon
= link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do
= image_tag "rss_ui.png", title: "feed"
:javascript
$(function(){
$('.project-refs-select').chosen();
});
......@@ -13,14 +13,11 @@
%td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- if @comments_allowed
= link_to "", "#", class: "line_note_link", "line_code" => line_code, title: "Add note for this line"
= render "notes/per_line_note_link", line_code: line_code
%td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
- if @comments_allowed
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at).reverse
- comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
- unless comments.empty?
- comments.each_with_index do |note, i|
= render "notes/reply_button", line_code: line_code if i.zero?
= render "notes/per_line_show", note: note
- @line_notes.reject!{ |n| n == note }
= render "notes/per_line_notes_with_reply", notes: comments
= render "head"
%h3
%h3.page_title
Compare View
%hr
%div
%p
%p.slead
Fill input field with commit id like
%code '4eedf23'
%code.label_branch 4eedf23
or branch/tag name like
%code master
&amp; press compare button for commits list, code diff.
%code.label_branch master
and press compare button for commits list, code diff.
%br
......@@ -19,22 +19,24 @@
= text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
= "..."
= text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "xlarge"
- if @refs_are_same
.alert
%span Refs are the same
.actions
= submit_tag "Compare", class: "btn btn-primary"
= submit_tag "Compare", class: "btn primary wide commits-compare-btn"
- unless @commits.empty?
- if @commits.present?
%div.ui-box
%h5.small Commits (#{@commits.count})
%ul.unstyled= render @commits
- unless @diffs.empty?
- unless @diffs.empty?
%h4 Diff
= render "commits/diffs", diffs: @diffs
:javascript
$(function() {
var availableTags = #{@project.heads.map(&:name).to_json};
var availableTags = #{@project.ref_names.to_json};
$("#from").autocomplete({
source: availableTags,
......@@ -45,5 +47,7 @@
source: availableTags,
minLength: 1
});
disableButtonIfEmptyField('#to', '.commits-compare-btn');
});
= render "commits/commit_box"
= render "commits/diffs", diffs: @commit.diffs
= render "notes/notes", tid: @commit.id, tt: "commit"
= render "notes/notes_with_form", tid: @commit.id, tt: "commit"
= render "notes/per_line_form"
:javascript
$(document).ready(function(){
$(".line_note_link, .line_note_reply_link").live("click", function(e) {
var form = $(".per_line_form");
$(this).parent().parent().after(form);
form.find("#note_line_code").val($(this).attr("line_code"));
form.show();
return false;
});
$(function(){
PerLineNotes.init();
});
......@@ -19,22 +19,31 @@
= link_to new_project_path, class: "btn very_small info" do
%i.icon-plus
New Project
%ul.unstyled
- @projects.each do |project|
%li.wll
= link_to project_path(project), class: dom_class(project) do
%h4
%span.ico.project
= truncate(project.name, length: 25)
%span.right
%strong.project_name= truncate(project.name, length: 25)
%span.arrow
&rarr;
%span.last_activity
%strong Last activity:
%span= project_last_activity(project)
.bottom= paginate @projects, theme: "gitlab"
%hr
%div
%span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
= image_tag "rss_ui.png", title: "feed"
%strong News Feed
%hr
.gitlab-promo
= link_to "Homepage", "http://gitlabhq.com"
= link_to "Blog", "http://blog.gitlabhq.com"
= link_to "@gitlabhq", "https://twitter.com/gitlabhq"
- else
%h3.nothing_here_message There are no projects you have access to.
%br
......
......@@ -11,8 +11,13 @@
.input= f.text_field :title
.clearfix
= 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
= f.submit 'Save', class: "primary btn"
= link_to "Cancel", project_deploy_keys_path(@project), class: "btn"
= f.submit 'Save', class: "save-btn btn"
= link_to "Cancel", project_deploy_keys_path(@project), class: "btn cancel-btn"
= render "repositories/head"
- if can? current_user, :admin_project, @project
.alert-message.block-message
Deploy keys allow read-only access to repository.
%p.slead
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
Add Deploy Key
- if @keys.any?
%table
%thead
%tr
%th Keys
%th
%th
- @keys.each do |key|
= render(partial: 'show', locals: {key: key})
= render "repositories/head"
%h3 New Deploy key
%h3.page_title New Deploy key
%hr
= render 'form'
= 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
%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"
.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
%h3 Encoding Error
%hr
%p
Page can't be loaded because of an encoding error.
%h1 Encoding Error
%hr
%p Page can't be loaded because of an encoding error.
%h1 Git Error
%hr
%h2 Gitlab was unable to access your Gitolite system.
%h2 GitLab was unable to access your Gitolite system.
.git_error_tips
%h4 Tips for Administrator:
......@@ -21,5 +21,5 @@
Permissions:
%pre
= preserve do
sudo chmod -R 770 /home/git/repositories/
sudo chown -R git:git /home/git/repositories/
sudo chmod -R 770 #{Gitlab.config.git_base_path}
sudo chown -R git:git #{Gitlab.config.git_base_path}
......@@ -5,4 +5,4 @@
%strong.cdark= commit.author_name
&ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm truncate(commit.title, length: 50) rescue "--broken encoding"
= gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
......@@ -11,3 +11,7 @@
.event_feed
= render "events/event_push", event: event
- elsif event.membership_changed?
.event_feed
= render "events/event_membership_changed", event: event
......@@ -9,5 +9,5 @@
at
%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
= 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
= link_to help_path do
&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
%a{href: "#README"} README
= link_to "Users", "#users", 'data-toggle' => 'tab'
%li
%a{href: "#projects"} Projects
= link_to "Session", "#session", 'data-toggle' => 'tab'
%li
%a{href: "#users"} Users
= link_to "Issues", "#issues", 'data-toggle' => 'tab'
%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
%i.icon-file
README
......@@ -22,9 +32,8 @@
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "README.md"))
%br
.file_holder#projects
.tab-pane#projects
.file_holder
.file_title
%i.icon-file
Projects
......@@ -32,9 +41,26 @@
= preserve do
= 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
%i.icon-file
Users
......@@ -42,12 +68,29 @@
= preserve do
= 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
%i.icon-file
Issues
.file_content.wiki
= preserve do
= 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 @@
%h5= link_to "API", help_api_path
%li
%h5= link_to "Gitlab Markdown", help_markdown_path
%h5= link_to "GitLab Markdown", help_markdown_path
%li
%h5= link_to "SSH keys", help_ssh_path
- bash_lexer = Pygments::Lexer[:bash]
%h3.page_title Gitlab Markdown
%h3.page_title GitLab Flavored Markdown
.back_link
= link_to help_path do
&larr; to index
%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
%li issues (#123)
%li merge request (!123)
%li commits (1234567)
%li team members (@foo)
%li snippets ($123)
%p.slead in
%ul
%p You can use GFM in:
%ul
%li commit messages
%li notes/comments/wall posts
%li comments
%li wall posts
%li issues
%li merge requests
%li milestones
%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 @@
%hr
%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
To generate a new SSH key just open your terminal and use code below.
......@@ -17,7 +17,7 @@
\# Generating public/private rsa key pair...
%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
cat ~/.ssh/id_rsa.pub
......
......@@ -5,7 +5,7 @@
%hr
%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
System Hooks can be used for logging or change information in LDAP server.
%br
......
......@@ -5,11 +5,11 @@
%hr
%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
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
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:
= 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