Commit 319f0c30 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into stable

Conflicts:
	doc/install/installation.md
parents e469084c ced242a2
......@@ -3,8 +3,11 @@ env:
- DB=mysql
before_install:
- sudo apt-get install libicu-dev -y
- sudo apt-get install libqt4-dev libqtwebkit-dev -y
- gem install charlock_holmes -v="0.6.8"
- wget -P /tmp http://phantomjs.googlecode.com/files/phantomjs-1.7.0-linux-i686.tar.bz2
- tar -xf /tmp/phantomjs-1.7.0-linux-i686.tar.bz2 -C /tmp/
- sudo rm -rf /usr/local/phantomjs
- sudo mv /tmp/phantomjs-1.7.0-linux-i686 /usr/local/phantomjs
- gem install charlock_holmes -v="0.6.9"
branches:
only:
- 'master'
......
v 3.1.0
- Updated gems
- Services: Gitlab CI integration
- Events filter on dashboard
- Own namespace for redis/resque
- Optimized commit diff views
- add alphabetical order for projects admin page
- Improved web editor
- Commit stats page
- Documentation split and cleanup
- Link to commit authors everywhere
- Restyled milestones list
- added Milestone to Merge Request
- Restyled Top panel
- Refactored Satellite Code
- Added file line links
- moved from capybara-webkit to poltergeist + phantomjs
v 3.0.3
- Fixed bug with issues list in Chrome
- New Feature: Import team from another project
......@@ -28,7 +46,7 @@ v 3.0.0
- Reject ssh keys that break gitolite
- [API] list one project hook
- [API] edit project hook
- [API] add project snippets list
- [API] list project snippets
- [API] allow to authorize using private token in HTTP header
- [API] add user creation
......
......@@ -15,16 +15,12 @@ We will only accept pull requests if:
* It's quality code
* We like it :)
## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
For examples of feedback on pull requests please look at the [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
## Running tests
To run the specs for GitLab, you need to run seeds for test db.
## Installation
cd gitlabhq
rake db:seed_fu RAILS_ENV=test
Install the Gitlab development in a virtual machine with the [Gitlab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm). Installing it in a virtual machine makes it much easier to set up all the dependencies for integration testing.
Then you can run the test suite with rake:
rake gitlab:test
## Running tests
For more information on running the tests please read the [development tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
......@@ -8,34 +8,35 @@ def linux_only(require_as)
RUBY_PLATFORM.include?('linux') && require_as
end
gem "rails", "3.2.8"
gem "rails", "3.2.9"
# Supported DBs
gem "sqlite3", :group => :sqlite
gem "mysql2", :group => :mysql
gem "pg", :group => :postgres
gem "sqlite3", group: :sqlite
gem "mysql2", group: :mysql
gem "pg", group: :postgres
# Auth
gem "devise", "~> 2.1.0"
gem 'omniauth'
gem 'omniauth', "~> 1.1.1"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
gem 'omniauth-github'
# GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
gem "omniauth-ldap", :git => "https://github.com/gitlabhq/omniauth-ldap.git", :ref => "f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e"
gem 'yaml_db', :git => "https://github.com/gitlabhq/yaml_db.git"
gem 'grack', :git => "https://github.com/gitlabhq/grack.git"
gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '7f35cb98ff17d534a07e3ce6ec3d580f67402837'
gem "omniauth-ldap", git: "https://github.com/gitlabhq/omniauth-ldap.git", ref: 'f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e'
gem 'yaml_db', git: "https://github.com/gitlabhq/yaml_db.git", ref: '98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd'
gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '212fd40bea61f3c6a167223768e7295dc32bbc10'
# Gitolite client (for work with gitolite-admin repo)
gem "gitolite", '1.1.0'
# Syntax highlighter
gem "pygments.rb", "0.3.1"
gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", ref: '4db80c599067e2d5f23c5c243bf85b8ca0368ad4'
# Language detection
gem "github-linguist", "~> 2.3.4" , :require => "linguist"
gem "github-linguist", "~> 2.3.4" , require: "linguist"
# API
gem "grape", "~> 0.2.1"
......@@ -45,13 +46,13 @@ gem "grape", "~> 0.2.1"
gem "stamp"
# Pagination
gem "kaminari"
gem "kaminari", "~> 0.14.1"
# HAML
gem "haml-rails"
gem "haml-rails", "~> 0.3.5"
# Files attachments
gem "carrierwave"
gem "carrierwave", "~> 0.7.1"
# Authorization
gem "six"
......@@ -63,59 +64,57 @@ gem "ffaker"
gem "seed-fu"
# Markdown to HTML
gem "redcarpet", "~> 2.1.1"
gem "redcarpet", "~> 2.2.2"
gem "github-markup", "~> 0.7.4", require: 'github/markup'
# Servers
gem "thin"
gem "unicorn"
gem "thin", '~> 1.5.0'
gem "unicorn", "~> 4.4.0"
# Issue tags
gem "acts-as-taggable-on", "2.3.1"
gem "acts-as-taggable-on", "2.3.3"
# Decorators
gem "draper"
gem "draper", "~> 0.18.0"
# Background jobs
gem "resque", "~> 1.20.0"
gem "resque", "~> 1.23.0"
gem 'resque_mailer'
# HTTP requests
gem "httparty"
# Handle encodings
gem "charlock_holmes"
# Colored output to console
gem "colored"
# GITLAB settings
# GitLab settings
gem 'settingslogic'
# Misc
gem "foreman"
gem 'gemoji', require: 'emoji/railtie'
gem "git"
group :assets do
gem "sass-rails", "3.2.5"
gem "coffee-rails", "3.2.2"
gem "uglifier", "1.0.3"
gem "sass-rails", "~> 3.2.5"
gem "coffee-rails", "~> 3.2.2"
gem "uglifier", "~> 1.3.0"
gem "therubyracer"
gem 'chosen-rails'
gem 'jquery-atwho-rails', '0.1.6'
gem "jquery-rails", "2.0.2"
gem "jquery-ui-rails", "0.5.0"
gem "modernizr", "2.5.3"
gem "raphael-rails", "1.5.2"
gem 'bootstrap-sass', "2.0.4"
gem 'chosen-rails', "0.9.8"
gem 'jquery-atwho-rails', "0.1.6"
gem "jquery-rails", "2.1.3"
gem "jquery-ui-rails", "2.0.2"
gem "modernizr", "2.6.2"
gem "raphael-rails", "2.1.0"
gem 'bootstrap-sass', "2.2.1.1"
gem "font-awesome-sass-rails", "~> 2.0.0"
gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
end
group :development do
gem "annotate", git: "https://github.com/ctran/annotate_models.git"
gem "letter_opener"
gem "annotate", :git => "https://github.com/ctran/annotate_models.git"
gem 'quiet_assets', '~> 1.0.1'
gem 'rack-mini-profiler'
end
......@@ -124,8 +123,6 @@ group :development, :test do
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara"
gem "capybara-webkit"
gem "headless"
gem "pry"
gem "awesome_print"
gem "database_cleaner"
......@@ -137,14 +134,17 @@ group :development, :test do
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')
gem 'rb-fsevent', require: darwin_only('rb-fsevent')
gem 'growl', require: darwin_only('growl')
gem 'rb-inotify', require: linux_only('rb-inotify')
# PhantomJS driver for Capybara
gem 'poltergeist'
end
group :test do
gem "simplecov", :require => false
gem "shoulda-matchers"
gem "simplecov", require: false
gem "shoulda-matchers", "1.3.0"
gem 'email_spec'
gem 'resque_spec'
gem "webmock"
......@@ -152,5 +152,5 @@ group :test do
end
group :production do
gem "gitlab_meta", '3.0'
gem "gitlab_meta", '3.1'
end
This diff is collapsed.
web: bundle exec rails s -p $PORT -e production
worker: bundle exec rake environment resque:work RAILS_ENV=production QUEUE=*
......@@ -13,7 +13,7 @@ GitLab is a free project and repository management application
* Ubuntu/Debian
* ruby 1.9.3+
* mysql or sqlite
* MySQL
* git
* gitolite
* redis
......
## GitLab Roadmap
### Common
* Help page for service tasks like repos import, backup etc
* Hide last push widget after following link
* Add comment events
* gitolite namespaces for projects per user/group. It will allow us same project names for different users
### Issues
* labels autocomplete via jquery autocomplete
* Import/Export issues
* Form: Assign to me link right to the selectbox
### Merge Request
* Save code fragments with MR comments
### Services
* Campfire integration service
* Hipchat integration service
* Travis CI integration service
* Jenkins CI integration service
app/assets/images/logo_dark.png

2.79 KB | W: | H:

app/assets/images/logo_dark.png

2.53 KB | W: | H:

app/assets/images/logo_dark.png
app/assets/images/logo_dark.png
app/assets/images/logo_dark.png
app/assets/images/logo_dark.png
  • 2-up
  • Swipe
  • Onion skin
app/assets/images/logo_white.png

1.64 KB | W: | H:

app/assets/images/logo_white.png

1.48 KB | W: | H:

app/assets/images/logo_white.png
app/assets/images/logo_white.png
app/assets/images/logo_white.png
app/assets/images/logo_white.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -13,10 +13,13 @@
//= require jquery.history
//= require jquery.waitforimages
//= require jquery.atwho
//= require jquery.scrollto
//= require bootstrap
//= require modernizr
//= require chosen-jquery
//= require raphael
//= require g.raphael-min
//= require g.bar-min
//= require branch-graph
//= require ace-src-noconflict/ace
//= require_tree .
# Creates the variables for setting up GFM auto-completion
window.GitLab ?= {}
GitLab.GfmAutoComplete ?= {}
###
Creates the variables for setting up GFM auto-completion
###
# Emoji
window.autocompleteEmojiData = [];
window.autocompleteEmojiTemplate = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>";
data = []
template = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>"
GitLab.GfmAutoComplete.Emoji = {data, template}
# Team Members
window.autocompleteMembersUrl = "";
window.autocompleteMembersParams =
private_token: ""
page: 1
window.autocompleteMembersData = [];
###
Add GFM auto-completion to all input fields, that accept GFM input.
###
window.setupGfmAutoComplete = ->
###
Emoji
###
$('.gfm-input').atWho ':',
data: autocompleteEmojiData,
tpl: autocompleteEmojiTemplate
###
Team Members
###
$('.gfm-input').atWho '@', (query, callback) ->
data = []
url = '';
params = {private_token: '', page: 1}
GitLab.GfmAutoComplete.Members = {data, url, params}
# Add GFM auto-completion to all input fields, that accept GFM input.
GitLab.GfmAutoComplete.setup = ->
input = $('.js-gfm-input')
# Emoji
input.atWho ':',
data: GitLab.GfmAutoComplete.Emoji.data,
tpl: GitLab.GfmAutoComplete.Emoji.template
# Team Members
input.atWho '@', (query, callback) ->
(getMoreMembers = ->
$.getJSON(autocompleteMembersUrl, autocompleteMembersParams)
$.getJSON(GitLab.GfmAutoComplete.Members.url, GitLab.GfmAutoComplete.Members.params)
.success (members) ->
# pick the data we need
newMembersData = $.map members, (m) -> m.name
newMembersData = $.map(members, (m) -> m.name )
# add the new page of data to the rest
$.merge autocompleteMembersData, newMembersData
$.merge(GitLab.GfmAutoComplete.Members.data, newMembersData)
# show the pop-up with a copy of the current data
callback autocompleteMembersData[..]
callback(GitLab.GfmAutoComplete.Members.data[..])
# are we past the last page?
if newMembersData.length == 0
if newMembersData.length is 0
# set static data and stop callbacks
$('.gfm-input').atWho '@',
data: autocompleteMembersData
input.atWho '@',
data: GitLab.GfmAutoComplete.Members.data
callback: null
else
# get next page
getMoreMembers()
# so the next request gets the next page
autocompleteMembersParams.page += 1;
).call();
\ No newline at end of file
GitLab.GfmAutoComplete.Members.params.page += 1
).call()
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
function switchToNewIssue(form){
function switchToNewIssue(){
$(".issues_content").hide("fade", { direction: "left" }, 150, function(){
$(".issues_content").after(form);
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
$("#new_issue_dialog").show("fade", { direction: "right" }, 150);
$('.top-tabs .add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
setupGfmAutoComplete();
GitLab.GfmAutoComplete.setup();
});
}
function switchToEditIssue(form){
function switchToEditIssue(){
$(".issues_content").hide("fade", { direction: "left" }, 150, function(){
$(".issues_content").after(form);
$('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen();
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
$('.add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
setupGfmAutoComplete();
GitLab.GfmAutoComplete.setup();
});
}
......@@ -33,18 +31,18 @@ function switchFromEditIssue(){
function backToIssues(){
$("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){
$(".issues_content").show("fade", { direction: "left" }, 150, function() {
$("#edit_issue_dialog").remove();
$("#new_issue_dialog").remove();
$("#edit_issue_dialog").html("");
$("#new_issue_dialog").html("");
$('.add_new').show();
});
});
}
function initIssuesSearch() {
var href = $('.issue_search').parent().attr('action');
var href = $('#issue_search_form').attr('action');
var last_terms = '';
$('.issue_search').keyup(function() {
$('#issue_search').keyup(function() {
var terms = $(this).val();
var milestone_id = $('#milestone_id').val();
var status = $('#status').val();
......@@ -59,10 +57,6 @@ function initIssuesSearch() {
}
}
});
$('.delete-issue').live('ajax:success', function() {
$(this).closest('tr').fadeOut(); updatePage();
});
}
/**
......
Loader =
html: (width) ->
$('<img>').attr src: '/assets/ajax-loader.gif', width: width
window.Loader = Loader
......@@ -7,29 +7,36 @@ window.slugify = (text) ->
window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"})
# Disable button if text field is empty
# 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 ""
field.on "input", ->
if $(@).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()
$(".one_click_select").on 'click', -> $(@).select()
# Initialize chosen selects
$('select.chosen').chosen()
# Initialize tooltips
$('.has_tooltip').tooltip()
# Bottom tooltip
$('.has_bottom_tooltip').tooltip(placement: 'bottom')
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', this)
buttons = $('[type="submit"]', @)
switch e.type
when 'ajax:beforeSend', 'submit'
......@@ -38,7 +45,7 @@ $ ->
buttons.enable()
# Show/Hide the profile menu when hovering the account box
$('.account-box').hover -> $(this).toggleClass('hover')
$('.account-box').hover -> $(@).toggleClass('hover')
# Focus search field by pressing 's' key
$(document).keypress (e) ->
......@@ -52,41 +59,22 @@ $ ->
# 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
$(@).next('table').show()
$(@).remove()
(($) ->
_chosen = $.fn.chosen
$.fn.extend chosen: (options) ->
default_options = search_contains: "true"
$.extend default_options, options
_chosen.apply this, [default_options]
_chosen.apply @, [default_options]
# Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: ->
$(this).attr('disabled', 'disabled').addClass('disabled')
$(@).attr('disabled', 'disabled').addClass('disabled')
# Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend enable: ->
$(this).removeAttr('disabled').removeClass('disabled')
$(@).removeAttr('disabled').removeClass('disabled')
)(jQuery)
......@@ -115,4 +115,15 @@ var MergeRequest = {
$(".merge_in_progress").hide();
$(".automerge_widget.already_cannot_be_merged").show();
}
};
/*
* Filter merge requests
*/
function merge_requestsPage() {
$("#assignee_id").chosen();
$("#milestone_id").chosen();
$("#milestone_id, #assignee_id").on("change", function(){
$(this).closest("form").submit();
});
}
......@@ -5,3 +5,10 @@ $ ->
$('.milestone-issue-filter li').toggleClass('active')
$('.milestone-issue-filter tr[data-closed]').toggleClass('hide')
false
$('.milestone-merge-requests-filter tr[data-closed]').addClass('hide')
$('.milestone-merge-requests-filter ul.nav li a').click ->
$('.milestone-merge-requests-filter li').toggleClass('active')
$('.milestone-merge-requests-filter tr[data-closed]').toggleClass('hide')
false
......@@ -14,8 +14,8 @@ var NoteList = {
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;
this.reversed = $("#notes-list").is(".reversed");
this.target_params = "target_type=" + this.target_type + "&target_id=" + this.target_id;
// get initial set of notes
this.getContent();
......@@ -33,6 +33,8 @@ var NoteList = {
$(".note-form-holder").on("ajax:complete", function(){
$(".submit_note").enable();
$('#preview-note').hide();
$('#note_note').show();
})
disableButtonIfEmptyField(".note-text", ".submit_note");
......@@ -52,6 +54,26 @@ var NoteList = {
$('.note_advanced_opts').show();
});
}
// Setup note preview
$(document).on('click', '#preview-link', function(e) {
$('#preview-note').text('Loading...');
$(this).text($(this).text() === "Edit" ? "Preview" : "Edit");
var note_text = $('#note_note').val();
if(note_text.trim().length === 0) {
$('#preview-note').text('Nothing to preview.');
} else {
$.post($(this).attr('href'), {note: note_text}).success(function(data) {
$('#preview-note').html(data);
});
}
$('#preview-note, #note_note').toggle();
e.preventDefault();
});
},
......@@ -69,7 +91,7 @@ var NoteList = {
$.ajax({
type: "GET",
url: this.notes_path,
data: "?" + this.target_params,
data: this.target_params,
complete: function(){ $('.notes-status').removeClass("loading")},
beforeSend: function() { $('.notes-status').addClass("loading") },
dataType: "script"});
......@@ -131,7 +153,7 @@ var NoteList = {
$.ajax({
type: "GET",
url: this.notes_path,
data: "loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id + this.target_params,
data: this.target_params + "&loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id,
complete: function(){ $('.notes-status').removeClass("loading")},
beforeSend: function() { $('.notes-status').addClass("loading") },
dataType: "script"});
......@@ -192,7 +214,7 @@ var NoteList = {
$.ajax({
type: "GET",
url: this.notes_path,
data: "loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id) + this.target_params,
data: this.target_params + "&loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id),
dataType: "script"});
},
......@@ -264,7 +286,7 @@ var PerLineNotes = {
$(this).closest("tr").after(form);
form.find("#note_line_code").val($(this).data("lineCode"));
form.show();
return false;
e.preventDefault();
});
disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
......@@ -285,7 +307,7 @@ var PerLineNotes = {
// 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")) {
if (trLine.is(".line_holder") && trRpl.is(".reply")) {
trRpl.fadeOut(function() { $(this).remove(); });
}
});
......
$ ->
$('.edit_user .application-theme input, .edit_user .code-preview-theme input').click ->
# Hide any previous submission feedback
$('.edit_user .update-feedback').hide()
# Submit the form
$('.edit_user').submit()
# Go up the hierarchy and show the corresponding submission feedback element
$(@).closest('fieldset').find('.update-feedback').show('highlight', {color: '#DFF0D8'}, 500)
......@@ -22,3 +22,10 @@ $ ->
# Ref switcher
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
class @GraphNav
@init: ->
$('.graph svg').css 'position', 'relative'
$('body').bind 'keyup', (e) ->
$('.graph svg').animate(left: '+=400') if e.keyCode is 37 # left
$('.graph svg').animate(left: '-=400') if e.keyCode is 39 # right
$ ->
$('#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
......@@ -17,9 +17,8 @@ $ ->
"ajax:beforeSend": -> $('.tree_progress').addClass("loading")
"ajax:complete": -> $('.tree_progress').removeClass("loading")
# Maintain forward/back history while browsing the file tree
((window) ->
# Maintain forward/back history while browsing the file tree
((window) ->
History = window.History
$ = window.jQuery
document = window.document
......@@ -28,7 +27,6 @@ $ ->
unless History.enabled
return false
$ ->
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live 'click', (e) ->
History.pushState(null, null, $(@).attr('href'))
return false
......@@ -36,4 +34,23 @@ $ ->
History.Adapter.bind window, 'statechange', ->
state = History.getState()
window.ajaxGet(state.url)
)(window)
)(window)
# See if there are lines selected
# "#L12" and "#L34-56" supported
highlightBlobLines = ->
if window.location.hash isnt ""
matches = window.location.hash.match(/\#L(\d+)(\-(\d+))?/)
first_line = parseInt(matches?[1])
last_line = parseInt(matches?[3])
unless isNaN first_line
last_line = first_line if isNaN(last_line)
$("#tree-content-holder .highlight .line").removeClass("hll")
$("#LC#{line}").addClass("hll") for line in [first_line..last_line]
$("#L#{first_line}").ScrollTo()
# Highlight the correct lines on load
highlightBlobLines()
# Highlight the correct lines when the hash part of the URL changes
$(window).on 'hashchange', highlightBlobLines
......@@ -20,18 +20,6 @@ body {
float:right;
}
.profile_avatar_holder {
float:left;
width:60px;
height:60px;
margin-right:20px;
img {
width:60px;
height:60px;
background:#eee;
}
}
.visible_link,
.author_link {
......@@ -596,25 +584,6 @@ li.note {
}
}
.themes_opts {
padding-left:20px;
label {
width:175px;
margin-right:40px;
.prev {
@extend .thumbnail;
height:120px;
width:175px;
margin-bottom:10px;
img {
width:180px;
}
}
}
}
.git_error_tips {
@extend .span6;
text-align:left;
......@@ -628,10 +597,11 @@ li.note {
.error_message {
@extend .cred;
border-bottom: 1px solid #D21;
padding-bottom:20px;
text-align:center;
margin-bottom:10px;
border-left: 4px solid #E99;
padding: 10px;
margin-bottom: 10px;
background: #FEE;
padding-left: 20px;
}
.oauth_select_holder {
......@@ -670,3 +640,16 @@ pre {
padding:0;
}
}
.milestone .progress {
margin-bottom: 0;
margin-top:4px;
}
.float-link {
float:left;
margin-right:15px;
.s16 {
margin-right:5px;
}
}
......@@ -94,6 +94,7 @@
&.very_small {
font-size:11px;
padding:2px 6px;
line-height: 16px;
margin:2px;
}
......
......@@ -26,8 +26,10 @@
.underlined { border-bottom: 1px solid #CCC; }
.no-borders { border:none; }
.vlink { color: $link_color !important; }
.underlined_link { text-decoration: underline; }
.borders { border: 1px solid #ccc; @include shade; }
.hint { font-style: italic; color: #999; }
.light { color: #888 }
/** PILLS & TABS**/
.nav-pills a:hover { background-color:#888; }
......@@ -38,6 +40,7 @@
> a {
padding:8px 20px;
margin-right: 7px;
line-height: 19px;
border-color: #EEE;
color:#888;
border-bottom: 1px solid #ddd;
......@@ -66,11 +69,12 @@
.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.avatar { float:left; margin-right:12px; width:40px; border:1px solid #ddd; padding:1px; }
img.avatar.s16 { width:16px; height:16px; margin-right:6px; }
img.avatar.s24 { width:24px; height:24px; margin-right:8px; }
img.avatar.s32 { width:32px; height:32px; margin-right:10px; }
img.lil_av { padding-left: 4px; padding-right:3px; }
img.small { width: 80px; }
/** HELPERS **/
.nothing_here_message { text-align:center; padding:20px; color:#777; }
......@@ -85,3 +89,5 @@ input[type='search'].search-text-input {
@include border-radius(4px);
border:1px solid #ccc;
}
fieldset legend { font-size: 17px; }
......@@ -132,35 +132,74 @@
* 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;
}
padding: 0;
table.lines {
border: none;
box-shadow: none;
margin: 0px;
padding: 0px;
table-layout: fixed;
table.highlighttable {
pre {
background: none;
border: none;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
font-size: 12px !important;
line-height: 16px !important;
margin: 0;
padding: 10px 0;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
td {
border: none;
margin: 0;
padding: 0;
vertical-align: top;
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
&:first-child {
background: #eee;
width: 50px;
}
&:last-child {
}
}
tr:hover {
background: none;
}
table.highlighttable .linenodiv pre {
pre.line_numbers {
color: #666;
padding: 10px 6px 10px 0;
text-align: right;
padding-right: 4px;
color:#666;
a {
color: #666;
i {
display: none;
font-size: 14px;
line-height: 14px;
}
&:hover i {
display: inherit;
}
}
}
.highlight {
border-left: 1px solid #DEE2E3;
overflow: auto;
overflow-y: hidden;
pre {
white-space: pre;
word-wrap: normal;
.line {
padding: 0 10px;
}
}
}
}
}
}
......
......@@ -21,7 +21,7 @@ ul {
.author { color: #999; }
p {
padding-top:5px;
padding-top: 1px;
margin:0;
color:#222;
img {
......@@ -31,3 +31,11 @@ ul {
}
}
}
ol, ul {
&.styled {
li {
padding:2px;
}
}
}
......@@ -34,6 +34,11 @@ table {
border-color:#f1f1f1;
line-height:28px;
.s16 {
margin-top: 5px;
margin-right: 5px;
}
&:first-child {
border-left:1px solid #bbb;
}
......
......@@ -2,8 +2,11 @@
* Headers
*
*/
h1, h2, h3, h4, h5, h6 { margin: 0; }
h3, h4, h5, h6 { line-height: 36px; }
h5 { font-size:14px; }
h3.page_title {
color:#456;
font-size:20px;
......@@ -11,6 +14,11 @@ h3.page_title {
line-height: 28px;
}
h6 {
color: #888;
text-transform: uppercase;
}
/** CODE **/
pre {
font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
......
.black .highlighttable {
td.linenos { border:none; }
pre { color: #eee }
.highlight { background: #333; border-left:1px solid #555; }
.black .lines .highlight {
background: #333;
pre { color: #eee; }
.hll { background-color: #ffffff }
.hll { display: block; background-color: darken($hover, 65%) }
.c { color: #888888; font-style: italic } /* Comment */
.err { color: #a61717; background-color: #e3d2d2 } /* Error */
.k { color: #CDA869; font-weight: bold } /* Keyword */
......@@ -22,43 +21,43 @@
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #606060 } /* Generic.Subheading */
.gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc{font-weight:bold;} /* Keyword.Constant */
.highlight .kd{font-weight:bold;} /* Keyword.Declaration */
.highlight .kn{font-weight:bold;} /* Keyword.Namespace */
.highlight .kp{font-weight:bold;} /* Keyword.Pseudo */
.highlight .kr{font-weight:bold;} /* Keyword.Reserved */
.highlight .kt{color:#458;font-weight:bold;} /* Keyword.Type */
.kc{font-weight:bold;} /* Keyword.Constant */
.kd{font-weight:bold;} /* Keyword.Declaration */
.kn{font-weight:bold;} /* Keyword.Namespace */
.kp{font-weight:bold;} /* Keyword.Pseudo */
.kr{font-weight:bold;} /* Keyword.Reserved */
.kt{color:#458;font-weight:bold;} /* Keyword.Type */
.m { color: #0000DD; font-weight: bold } /* Literal.Number */
.p { color: #eee; }
.s { color: #0AD; background-color: transparent } /* Literal.String */
.highlight .na{color:#008080;} /* Name.Attribute */
.highlight .nb{color:#0086B3;} /* Name.Builtin */
.highlight .nc{color:#ccc;font-weight:bold;} /* Name.Class */
.highlight .no{color:turquoise;} /* Name.Constant */
.highlight .ni{color:#800080;}
.highlight .ne{color:#900;font-weight:bold;} /* Name.Exception */
.highlight .nf{color:#ccc;font-weight:bold;} /* Name.Function */
.highlight .nn{color:#79C3E0;font-weight:bold;} /* Name.Namespace */
.highlight .nt{color:#fc5;} /* Name.Tag */
.highlight .nv{color:#FA4;} /* Name.Variable */
.na{color:#008080;} /* Name.Attribute */
.nb{color:#0086B3;} /* Name.Builtin */
.nc{color:#ccc;font-weight:bold;} /* Name.Class */
.no{color:turquoise;} /* Name.Constant */
.ni{color:#800080;}
.ne{color:#900;font-weight:bold;} /* Name.Exception */
.nf{color:#ccc;font-weight:bold;} /* Name.Function */
.nn{color:#79C3E0;font-weight:bold;} /* Name.Namespace */
.nt{color:#fc5;} /* Name.Tag */
.nv{color:#FA4;} /* Name.Variable */
.py { color: #336699; font-weight: bold } /* Name.Property */
.ow { color: #008800 } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #7AC; font-weight: bold } /* Literal.Number.Float */
.mh { color: #7AC; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi {color:#099;} /* Literal.Number.Integer */
.mi {color:#099;} /* Literal.Number.Integer */
.mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.sb { color: #dd2200; background-color: transparent; } /* Literal.String.Backtick */
.highlight .sc{color:#d14;} /* Literal.String.Char */
.sc{color:#d14;} /* Literal.String.Char */
.sd { color: #dd2200; background-color: transparent; } /* Literal.String.Doc */
.highlight .s2{color:orange;} /* Literal.String.Double */
.highlight .se{color:orange;} /* Literal.String.Escape */
.highlight .sh{color:orange;} /* Literal.String.Heredoc */
.highlight .si{color:orange;} /* Literal.String.Interpol */
.highlight .sx{color:orange;} /* Literal.String.Other */
.highlight .sr{color:orange;} /* Literal.String.Regex */
.highlight .s1{color:orange;} /* Literal.String.Single */
.highlight .ss{color:orange;} /* Literal.String.Symbol */
.s2{color:orange;} /* Literal.String.Double */
.se{color:orange;} /* Literal.String.Escape */
.sh{color:orange;} /* Literal.String.Heredoc */
.si{color:orange;} /* Literal.String.Interpol */
.sx{color:orange;} /* Literal.String.Other */
.sr{color:orange;} /* Literal.String.Regex */
.s1{color:orange;} /* Literal.String.Single */
.ss{color:orange;} /* Literal.String.Symbol */
.bp { color: #D58 } /* Name.Builtin.Pseudo */
.vc { color: #336699 } /* Name.Variable.Class */
.vg { color: #dd7700 } /* Name.Variable.Global */
......
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;
margin:0;
border-top:0;
vertical-align:top;
}
.highlighttable .highlight{
background:none;
padding:10px 0px 0px 10px;
margin-left:0px;
border-left: 1px solid #DEE2E3;
.white .lines .highlight {
background: white;
}
.linenodiv pre,
.highlighttable .highlight pre{
margin:0;
padding:0;
background:none;
border:none;
}
pre { color: #333; }
.linenodiv pre {
white-space:pre-line;
.hll { display: block; background-color: $hover }
.c { color: #888888; font-style: italic } /* Comment */
.err { color: #a61717; background-color: #e3d2d2 } /* Error */
.k { color: #000000; font-weight: bold } /* Keyword */
.cm { color: #888888 } /* Comment.Multiline */
.cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.c1 { color: #888888 } /* Comment.Single */
.cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #aa0000 } /* Generic.Error */
.gh { color: #303030 } /* Generic.Heading */
.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.go { color: #888888 } /* Generic.Output */
.gp { color: #555555 } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #606060 } /* Generic.Subheading */
.gt { color: #aa0000 } /* Generic.Traceback */
.kc{font-weight:bold;} /* Keyword.Constant */
.kd{font-weight:bold;} /* Keyword.Declaration */
.kn{font-weight:bold;} /* Keyword.Namespace */
.kp{font-weight:bold;} /* Keyword.Pseudo */
.kr{font-weight:bold;} /* Keyword.Reserved */
.kt{color:#458;font-weight:bold;} /* Keyword.Type */
.m { color: #0000DD; font-weight: bold } /* Literal.Number */
.s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.na{color:#008080;} /* Name.Attribute */
.nb{color:#0086B3;} /* Name.Builtin */
.nc{color:#458;font-weight:bold;} /* Name.Class */
.no{color:#008080;} /* Name.Constant */
.ni{color:#800080;}
.ne{color:#900;font-weight:bold;} /* Name.Exception */
.nf{color:#900;font-weight:bold;} /* Name.Function */
.nn{color:#005;font-weight:bold;} /* Name.Namespace */
.nt{color:#000080;} /* Name.Tag */
.nv{color:#008080;} /* Name.Variable */
.py { color: #336699; font-weight: bold } /* Name.Property */
.ow { color: #008800 } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.mi {color:#099;} /* Literal.Number.Integer */
.mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.sc{color:#d14;} /* Literal.String.Char */
.sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.s2{color:#d14;} /* Literal.String.Double */
.se{color:#d14;} /* Literal.String.Escape */
.sh{color:#d14;} /* Literal.String.Heredoc */
.si{color:#d14;} /* Literal.String.Interpol */
.sx{color:#d14;} /* Literal.String.Other */
.sr{color:#d14;} /* Literal.String.Regex */
.s1{color:#d14;} /* Literal.String.Single */
.ss{color:#d14;} /* Literal.String.Symbol */
.bp { color: #003388 } /* Name.Builtin.Pseudo */
.vc { color: #336699 } /* Name.Variable.Class */
.vg { color: #dd7700 } /* Name.Variable.Global */
.vi { color: #3333bb }
}
td.linenos {
/*background:#F7F7F7;*/
color:#666;
padding:10px 0px 0px 10px;
float:left;
width:45px;
border-right: 1px solid #ccc;
}
td.code .highlight {
overflow: auto;
}
table.highlighttable pre{
padding:0;
margin:0;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
color: #333;
text-align:left;
}
.git-empty .highlight {
pre{
padding:15px;
line-height:2.0;
margin:0;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
color: #333;
text-align:left;}
}
.shadow{
.shadow {
-webkit-box-shadow:0 5px 15px #000;
-moz-box-shadow:0 5px 15px #000;
box-shadow:0 5px 15px #000;
}
.hll { background-color: #ffffff }
.c { color: #888888; font-style: italic } /* Comment */
.err { color: #a61717; background-color: #e3d2d2 } /* Error */
.k { color: #000000; font-weight: bold } /* Keyword */
.cm { color: #888888 } /* Comment.Multiline */
.cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.c1 { color: #888888 } /* Comment.Single */
.cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #aa0000 } /* Generic.Error */
.gh { color: #303030 } /* Generic.Heading */
.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.go { color: #888888 } /* Generic.Output */
.gp { color: #555555 } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #606060 } /* Generic.Subheading */
.gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc{font-weight:bold;} /* Keyword.Constant */
.highlight .kd{font-weight:bold;} /* Keyword.Declaration */
.highlight .kn{font-weight:bold;} /* Keyword.Namespace */
.highlight .kp{font-weight:bold;} /* Keyword.Pseudo */
.highlight .kr{font-weight:bold;} /* Keyword.Reserved */
.highlight .kt{color:#458;font-weight:bold;} /* Keyword.Type */
.m { color: #0000DD; font-weight: bold } /* Literal.Number */
.s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na{color:#008080;} /* Name.Attribute */
.highlight .nb{color:#0086B3;} /* Name.Builtin */
.highlight .nc{color:#458;font-weight:bold;} /* Name.Class */
.highlight .no{color:#008080;} /* Name.Constant */
.highlight .ni{color:#800080;}
.highlight .ne{color:#900;font-weight:bold;} /* Name.Exception */
.highlight .nf{color:#900;font-weight:bold;} /* Name.Function */
.highlight .nn{color:#005;font-weight:bold;} /* Name.Namespace */
.highlight .nt{color:#000080;} /* Name.Tag */
.highlight .nv{color:#008080;} /* Name.Variable */
.py { color: #336699; font-weight: bold } /* Name.Property */
.ow { color: #008800 } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi {color:#099;} /* Literal.Number.Integer */
.mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc{color:#d14;} /* Literal.String.Char */
.sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2{color:#d14;} /* Literal.String.Double */
.highlight .se{color:#d14;} /* Literal.String.Escape */
.highlight .sh{color:#d14;} /* Literal.String.Heredoc */
.highlight .si{color:#d14;} /* Literal.String.Interpol */
.highlight .sx{color:#d14;} /* Literal.String.Other */
.highlight .sr{color:#d14;} /* Literal.String.Regex */
.highlight .s1{color:#d14;} /* Literal.String.Single */
.highlight .ss{color:#d14;} /* Literal.String.Symbol */
.bp { color: #003388 } /* Name.Builtin.Pseudo */
.vc { color: #336699 } /* Name.Variable.Class */
.vg { color: #dd7700 } /* Name.Variable.Global */
.vi { color: #3333bb }
/** Override bootstrap variables **/
$baseFontSize: 13px !default;
$baseLineHeight: 18px !default;
@import "bootstrap";
@import "bootstrap-responsive";
@import 'font-awesome';
/** GitLab colors **/
$link_color:#3A89A3;
$blue_link: #2fa0bb;
$style_color: #474d57;
$link_color: #3A89A3;
$blue_link: #2FA0BB;
$style_color: #474D57;
$hover: #D9EDF7;
$hover_border: #ADF;
/** GitLab Fonts **/
@font-face { font-family: Korolev; src: font-url('korolev-medium-compressed.otf'); }
$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
/** MIXINS **/
@mixin shade {
......@@ -19,9 +25,9 @@ $hover: #D9EDF7;
}
@mixin solid_shade {
-moz-box-shadow: 0 0 0 3px #eee;
-webkit-box-shadow: 0 0 0 3px #eee;
box-shadow: 0 0 0 3px #eee;
-moz-box-shadow: 0 0 0 3px #f1f1f1;
-webkit-box-shadow: 0 0 0 3px #f1f1f1;
box-shadow: 0 0 0 3px #f1f1f1;
}
@mixin border-radius($radius) {
......@@ -64,6 +70,14 @@ $hover: #D9EDF7;
background-image: -o-linear-gradient($from, $to);
}
@mixin bg-light-gray-gradient {
background:#f1f1f1;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
}
@mixin bg-gray-gradient {
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
......@@ -104,14 +118,12 @@ $hover: #D9EDF7;
@import "themes/ui_basic.scss";
/**
* UI mars theme
* UI themes:
*/
@import "themes/ui_mars.scss";
/**
* UI Modern theme
*/
@import "themes/ui_modern.scss";
@import "themes/ui_gray.scss";
@import "themes/ui_color.scss";
/**
* GitLab bootstrap.
......@@ -145,6 +157,7 @@ $hover: #D9EDF7;
@import "sections/merge_requests.scss";
@import "sections/graph.scss";
@import "sections/events.scss";
@import "sections/themes.scss";
/**
* This scss file redefine chozen selectbox styles for
......
......@@ -19,31 +19,66 @@
margin-right: 10px;
.chzn-drop {
margin:7px 0;
min-width: 400px;
border: 2px solid $blue_link;
@include border-radius(4px);
.chzn-results {
max-height:300px;
}
.chzn-search input {
min-width:365px;
}
}
}
/** Fix for Search Dropdown Border **/
.chzn-container {
.chzn-search {
input:focus {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
.chzn-drop {
margin:7px 0;
min-width: 200px;
border: 1px solid #bbb;
border-radius:0;
.chzn-results {
margin-top: 5px;
max-height:300px;
.group-result {
color: $blue_link;
color: $style_color;
border-bottom: 1px solid #EEE;
padding: 8px;
}
.active-result {
border-radius: 0;
&.highlighted {
background: $blue_link;
background: $hover;
color: $style_color;
}
&.result-selected {
background: #EEE;
border-left: 4px solid #CCC;
}
}
}
.chzn-search input {
min-width:365px;
.chzn-search {
@include bg-gray-gradient;
input {
min-width:165px;
border-color: #CCC;
}
}
}
.chzn-single {
@include bg-gray-gradient;
@include bg-light-gray-gradient;
div {
background:transparent;
......@@ -55,14 +90,3 @@
}
}
}
/** Fix for Search Dropdown Border **/
.chzn-container {
.chzn-search {
input:focus {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
}
......@@ -47,12 +47,15 @@
padding-left: 32px;
}
.author,
.committer {
.author a,
.committer a {
font-size:14px;
line-height:22px;
text-shadow:0 1px 1px #fff;
color:#777;
&:hover {
color: #999;
}
}
.avatar {
......@@ -71,7 +74,9 @@
margin-bottom:1em;
.diff_file_header {
padding:7px 5px;
@extend .clearfix;
padding: 5px 5px 5px 10px;
color: #555;
border-bottom:1px solid #CCC;
background: #eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
......@@ -79,8 +84,23 @@
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
span {
> span {
font-family: $monospace;
font-size:14px;
line-height: 30px;
}
a.view-commit{
font-weight: bold;
}
.commit-short-id{
font-family: $monospace;
font-size: smaller;
}
.file-mode{
font-family: $monospace;
}
}
.diff_file_content {
......@@ -89,7 +109,7 @@
background:#fff;
color:#333;
font-size: 12px;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
font-family: $monospace;
.old{
span.idiff{
background-color:#FAA;
......@@ -110,22 +130,34 @@
.diff_file_content_image {
background:#eee;
text-align:center;
img {
.image {
display: inline-block;
margin:50px;
padding:1px;
max-width:400px;
&.diff_image_removed {
img{
background: url('trans_bg.gif');
}
&.diff_removed {
img{
border: 1px solid #C00;
}
}
&.diff_image_added {
border: 1px solid #0C0;;
&.diff_added {
img{
border: 1px solid #0C0;
}
}
.image-info{
margin: 5px 0 0 0;
}
}
&.img_compared {
img {
.image {
max-width:300px;
}
}
......@@ -222,26 +254,41 @@
float:left;
@extend .lined;
min-width:65px;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
font-family: $monospace;
}
.commit-author-name {
color: #777;
&:hover {
color: #999;
}
}
}
.diff_file_header a,
.file_stats a {
color:$style_color;
.file-stats a {
color: $style_color;
}
.file_stats {
span {
img {
width:14px;
float:left;
margin-right:6px;
padding:2px 0;
.file-stats {
.new-file{
i{
color: #1BCF00;
}
}
.renamed-file{
i{
color: #FE9300;
}
}
.deleted-file{
i{
color: #FF0000;
}
}
.edit-file{
i{
color: #555;
}
}
}
......@@ -253,5 +300,5 @@
font-size:13px;
background: #474D57;
color:#fff;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
font-family: $monospace;
}
.file-editor {
#editor{
border: none;
border-radius: 0;
height: 500px;
width: 100%;
margin: 0;
padding: 0;
position: relative;
width: 100%;
}
.cancel-btn {
color: #B94A48;
&:hover {
color: #B94A48;
}
}
.commit-button-annotation {
@extend .alert;
@extend .alert-info;
display: inline-block;
margin: 0;
padding: 2px;
> * {
float: left;
}
.commit-btn {
@extend .save-btn;
}
.message {
display: inline-block;
margin: 5px 8px 0 8px;
}
}
.commit_message-group {
margin-top: 20px;
label {
font-size: 16px;
line-height: 20px;
}
.editor-commit-comment {
padding-top:20px;
textarea {
width: 50%;
margin-left: 20px;
@extend .span8;
}
}
}
......@@ -43,6 +43,7 @@
.event-body {
p {
color:#555;
padding-top: 5px;
}
.event-info {
color:#666;
......@@ -115,3 +116,29 @@
margin: -3px;
}
}
/**
* Event filter
*
*/
.event_filter {
position: absolute;
width: 40px;
margin-left: -50px;
.filter_icon {
float: left;
border-left: 3px solid #4bc;
padding: 7px;
background: #f9f9f9;
margin-bottom: 10px;
img {
width:20px;
}
&.inactive {
border-left: 3px solid #EEE;
opacity: 0.5;
}
}
}
......@@ -3,18 +3,29 @@
*
*/
header {
width:100%;
padding:0;
margin:0;
top:1px;
left:0;
background: #F1F1F1; /* for non-css3 browsers */
border-bottom: 1px solid #ccc;
box-shadow: 0 -1px 0 white inset;
-moz-box-shadow: 0 -1px 0 white inset;
-webkit-box-shadow: 0 -1px 0 white inset;
&.navbar-gitlab {
.navbar-inner {
height:45px;
padding: 5px;
background: #F1F1F1;
.nav > li > a {
color: $style_color;
text-shadow: 0 1px 0 #fff;
font-size: 18px;
padding: 11px;
}
/** NAV block with links and profile **/
.nav {
float: right;
margin-right: 0;
}
}
}
z-index:10;
height:60px;
/*height:60px;*/
/**
*
......@@ -22,45 +33,26 @@ header {
*
*/
.app_logo {
width:200px;
width:170px;
float:left;
position:relative;
top:-5px;
a {
float:left;
padding: 0px;
h1 {
padding-top: 5px;
width:90px;
background: url('logo_dark.png') no-repeat 0px -3px;
background: url('logo_dark.png') no-repeat 0px 2px;
float:left;
margin-left:5px;
font-size:36px;
line-height:36px;
margin-left:2px;
font-size:30px;
line-height:48px;
font-weight:normal;
color:$style_color;
text-shadow: 0 1px 1px #FFF;
padding-left:50px;
padding-left:45px;
height:40px;
font-family: 'Korolev', sans-serif;
}
}
.separator {
margin-left:20px;
float: left;
height: 60px;
width: 1px;
background: white;
border-left: 1px solid #DDD;
margin-top: -10px;
}
}
.container {
.top_panel_content {
margin:auto;
position:relative;
padding:15px 0;
}
}
......@@ -74,33 +66,23 @@ header {
float:left;
margin:0;
margin-right:30px;
font-size:36px;
line-height:36px;
font-size:30px;
line-height:48px;
font-weight:normal;
color:$style_color;
text-shadow: 0 1px 1px #FFF;
font-family: 'Korolev', sans-serif;
}
.fbtn {
float: right;
margin-right:10px;
.btn {
margin-left:7px;
background: #F1F1F1;
border: 1px solid #CCC;
}
}
/**
*
* Search box
*
*/
.search {
float: right;
margin-right: 45px;
margin-left:10px;
margin-top: 2px;
.search-input {
@extend .span2;
......@@ -108,8 +90,13 @@ header {
background-repeat: no-repeat;
background-position: 10px;
padding-left:25px;
@include border-radius(5px);
border:1px solid #ccc;
font-size: 13px;
@include border-radius(3px);
border:1px solid #c6c6c6;
box-shadow:none;
&:focus {
@extend .span3;
}
}
}
......@@ -121,7 +108,7 @@ header {
.account-box {
position: absolute;
right: 0;
top: 13px;
top: 6px;
z-index: 10000;
width: 128px;
font-size: 11px;
......@@ -129,13 +116,13 @@ header {
display: block;
cursor: pointer;
img {
@include border-radius(4px);
@include border-radius(3px);
right: 5px;
position: absolute;
width: 28px;
height: 28px;
display: block;
top: 2px;
top:1px;
&:after {
content: " ";
display: block;
......@@ -162,12 +149,7 @@ header {
display: block; } }
.account-links {
background: #79C3E0;
display: none;
border-radius: 5px;
width: 100px;
margin-top: 0;
float: right;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
position: relative;
&:before {
......@@ -177,32 +159,33 @@ header {
position: absolute;
border: 5px solid transparent;
border-color: rgba(255, 255, 255, 0);
border-bottom-color: #333;
border-bottom-color: #555;
text-indent: -9999px;
top: -10px;
line-height: 0;
right: 10px;
z-index: 10; }
background: #333;
background: #555;
display: none;
z-index: 100000;
border-radius: 5px;
@include border-radius(4px);
width: 100px;
position: absolute;
right: 10px;
top: 42px;
right: 5px;
top: 38px;
margin-top: 0;
float: right;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
a {
color: #EEE;
padding: 6px 10px;
color: #fff;
padding: 7px 10px;
display: block;
text-shadow: none;
border-bottom: 1px solid #555;
border-bottom: 1px solid #666;
font-size: 12px;
&:hover {
color:#eee;
background: #444;
color:#fff;
background: #333;
}
}
}
......@@ -228,5 +211,52 @@ header {
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
border-bottom: 0; } }
/*
* Dark header
*
*/
&.header-dark {
&.navbar-gitlab {
.navbar-inner {
background: #708090;
border-bottom: 1px solid #AAA;
.nav > li > a {
color: #fff;
text-shadow: 0 1px 0 #111;
}
}
}
.search {
.search-input {
background-color: #D2D5DA;
background-color: rgba(255, 255, 255, 0.5);
&:focus {
background-color: white;
}
}
}
.search-input::-webkit-input-placeholder {
color: #666;
}
.app_logo {
a {
h1 {
background: url('logo_white.png') no-repeat 0px 2px;
color:#fff;
text-shadow: 0 1px 1px #111;
}
}
}
.project_name {
color:#fff;
text-shadow: 0 1px 1px #111;
}
}
}
......@@ -44,7 +44,7 @@
img.avatar {
width:32px;
margin-top:4px;
margin-top:1px;
}
}
}
......
......@@ -71,7 +71,7 @@ li.merge_request {
padding:7px 10px;
img.avatar {
width: 32px;
margin-top: 4px;
margin-top: 1px;
}
p {
padding: 0px;
......@@ -121,3 +121,25 @@ li.merge_request {
.mr_direction_tip {
margin-top:40px
}
.merge_requests_form_box {
@extend .main_box;
.merge_requests_middle_box {
@extend .middle_box_content;
height:30px;
.merge_requests_assignee {
@extend .span6;
float:left;
}
.merge_requests_milestone {
@extend .span4;
float:left;
}
}
}
.status-badge {
height: 32px;
width: 100%;
@include border-radius(5px);
}
......@@ -6,7 +6,7 @@ ul.main_menu {
border-radius: 4px;
margin: auto;
margin:30px 0;
border:1px solid #AAA;
border:1px solid #BBB;
height:37px;
@include bg-gray-gradient;
position:relative;
......
......@@ -6,3 +6,17 @@
}
}
}
.profile_avatar_holder {
float:left;
width:60px;
height:60px;
margin-right:20px;
img {
width:60px;
height:60px;
background:#fff;
padding: 1px;
border: 1px solid #ddd;
}
}
......@@ -85,9 +85,18 @@
}
.project_clone_holder {
input[type="text"],
.btn {
font-size:12px;
line-height: 18px;
margin: 0;
padding: 3px 10px;
}
input[type="text"] {
border: 1px solid #BBB;
box-shadow: none;
margin-left: -1px;
}
}
......
.application-theme, .code-preview-theme {
.update-feedback {
color: #468847;
float: right;
}
}
.themes_opts {
padding-left:20px;
label {
width:175px;
margin-right:40px;
.prev {
@extend .thumbnail;
height:30px;
width:175px;
margin-bottom:10px;
&.classic {
background: #31363e;
}
&.default {
background: #f1f1f1;
}
&.modern {
background: #567;
}
&.gray {
background: #708090;
}
&.violet {
background: #657;
}
}
}
}
.code_highlight_opts {
padding-left:20px;
label {
width:220px;
margin-right:40px;
.prev {
@extend .thumbnail;
height:151px;
width:220px;
margin-bottom:10px;
}
}
}
......@@ -57,10 +57,7 @@
padding-right: 8px;
img.avatar {
border: 0 none;
float: none;
margin-right: 0;
padding: 0;
margin-top: 0;
width: 16px;
}
}
......@@ -75,6 +72,15 @@
}
}
}
.blame {
img.avatar {
border: 0 none;
float: none;
margin: 0;
padding: 0;
}
}
}
.tree-btn-group {
......
......@@ -16,35 +16,21 @@
}
}
header {
.fbtn {
.btn {
background-color: #F8F8F8;
background-image: -webkit-gradient(linear,left top,left bottom,from(#F8F8F8),to(#ECECEC));
background-image: -webkit-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: -moz-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: -ms-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: -o-linear-gradient(top,#F8F8F8,#ECECEC);
background-image: linear-gradient(top,#F8F8F8,#ECECEC);
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#f8f8f8',EndColorStr='#ececec');
border-color: #C6C6C6;
margin-left:7px;
@include border-radius(3px);
box-shadow:none;
color:#666;
}
}
.search {
.search-input {
@include border-radius(3px);
border-color: #C6C6C6;
box-shadow:none;
}
}
.pic {
img {
@include border-radius(3px);
.app_logo {
.separator {
margin-left: 0;
margin-right: 0;
}
}
.separator {
float: left;
height: 60px;
width: 1px;
background: white;
border-left: 1px solid #DDD;
margin-top: -10px;
margin-left: 10px;
margin-right: 10px;
}
}
/**
* This file represent some UI that can be changed
* during web app restyle or theme select.
*
* Next items should be placed there
* - link colors
* - header restyles
*
*/
.ui_color {
/*
* Application Header
*
*/
header {
@extend .header-dark;
&.navbar-gitlab {
.navbar-inner {
background: #657;
}
}
}
}
/**
* This file represent some UI that can be changed
* during web app restyle or theme select.
*
* Next items should be placed there
* - link colors
* - header restyles
*
*/
.ui_gray {
/*
* Application Header
*
*/
header {
@extend .header-dark;
&.navbar-gitlab {
.navbar-inner {
background: #708090;
}
}
}
}
......@@ -14,45 +14,24 @@
*
*/
header {
&.navbar-gitlab {
.navbar-inner {
background: #474D57 url('bg-header.png') repeat-x bottom;
box-shadow:none;
border-bottom: 1px solid #444;
.fbtn {
.btn {
i {
position: relative;
top: 1px;
.nav > li > a {
color: #eee;
text-shadow: 0 1px 0 #444;
}
margin-left:8px;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595D63), to(#31363E));
background-image: -webkit-linear-gradient(#595D63 6.6%, #31363E);
background-image: -moz-linear-gradient(#595D63 6.6%, #31363E);
background-image: -o-linear-gradient(#595D63 6.6%, #31363E);
font-size: 12px;
&:hover {
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595D63), to(#2C2F35));
background-image: -webkit-linear-gradient(#595D63 6.6%, #2C2F35);
background-image: -moz-linear-gradient(#595D63 6.6%, #202227);
background-image: -o-linear-gradient(#595D63 6.6%, #202227);
background-position:0 0;
color:#fff;
i {
@extend .icon-white;
}
}
border: 1px solid #31363E;
color:#D6DADF;
text-shadow: 0 -1px 0 #000000;
}
}
.search {
float: right;
margin-right: 45px;
.search-input {
border: 1px solid rgba(0, 0, 0, 0.7);
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2), 0 2px 2px rgba(0, 0, 0, 0.4) inset;
background-color: #D2D5DA;
background-color: rgba(255, 255, 255, 0.5);
......@@ -67,8 +46,8 @@
.app_logo {
a {
h1 {
background: url('logo_white.png') no-repeat 0px -3px;
color:#fff;
background: url('logo_white.png') no-repeat 0px 2px;
color:#eee;
text-shadow: 0 1px 1px #111;
}
}
......@@ -78,7 +57,7 @@
}
.project_name {
color:#fff;
color:#eee;
text-shadow: 0 1px 1px #111;
}
}
......
......@@ -4,8 +4,7 @@
*
* Next items should be placed there
* - link colors
* - header styles
* - main menu styles
* - header restyles
*
*/
.ui_modern {
......@@ -14,120 +13,10 @@
*
*/
header {
height:40px;
background-image: -moz-linear-gradient(top, #333, #222);
background-image: -ms-linear-gradient(top, #333, #222);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333), to(#222));
background-image: -webkit-linear-gradient(top, #333, #222);
background-image: -o-linear-gradient(top, #333, #222);
background-image: linear-gradient(top, #333, #222);
background-repeat: repeat-x;
background-repeat: repeat-x;
filter: progid:dximagetransform.microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
-moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1);
.container .top_panel_content { padding: 5px 0; }
/**
*
* Logo holder
*
*/
.app_logo {
width:160px;
a {
h1 {
background: none;
color:#DDD;
font-size:30px;
text-shadow: 0 1px 1px #111;
padding-left: 0;
}
}
.separator {
width: 1px;
height: 40px;
margin: 0 10px;
overflow: hidden;
background: #222;
border-left: 1px solid #333;
}
}
.fbtn {
.btn {
i {
position: relative;
top: 2px;
}
background:none;
margin-left:8px;
font-size: 13px;
line-height: 19px;
color:#ccc;
&:hover {
color:#fff;
i {
@extend .icon-white;
}
}
border: none;
box-shadow:none;
text-shadow: 0 -1px 0 #000000;
border-left: 1px solid #333;
}
}
/**
*
* Search box
*
*/
.search {
float: right;
margin-right: 45px;
.search-input {
border: 1px solid rgba(0, 0, 0, 0.7);
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.2), 0 2px 2px rgba(0, 0, 0, 0.4) inset;
background-color: #D2D5DA;
background-color: rgba(255, 255, 255, 0.5);
&:focus {
background-color: white;
}
}
.search-input::-webkit-input-placeholder {
color: #666;
}
}
/**
*
* Project / Area name
*
*/
.project_name {
line-height:36px;
font-size:30px;
color:#DDD;
text-shadow: 0 1px 1px #111;
}
/**
*
* Account box
*
*/
.account-box {
top:6px;
img {
top:1px;
right: 5px;
width: 26px;
height: 26px;
@extend .header-dark;
&.navbar-gitlab {
.navbar-inner {
background: #567;
}
}
}
......
......@@ -21,7 +21,7 @@ class CommitLoadContext < BaseContext
result[:notes_count] = line_notes.count + project.commit_notes(commit).count
begin
result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff]
result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff]
rescue Grit::Git::GitTimeout
result[:suppress_diff] = true
result[:status] = :huge_commit
......
# Build collection of Merge Requests
# based on filtering passed via params for @project
class MergeRequestsLoadContext < BaseContext
def execute
type = params[:f]
......@@ -9,8 +11,21 @@ class MergeRequestsLoadContext < BaseContext
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)
end
merge_requests = merge_requests.page(params[:page]).per(20)
merge_requests = merge_requests.includes(:author, :project).order("closed, created_at desc")
# Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present?
merge_requests = merge_requests.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id]))
end
# Filter by specific milestone_id (or lack thereof)?
if params[:milestone_id].present?
merge_requests = merge_requests.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
end
merge_requests.includes(:author, :project).order("closed, created_at desc")
merge_requests
end
end
......@@ -13,6 +13,7 @@ class SearchContext
result[:projects] = Project.where(id: project_ids).search(query).limit(10)
result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(10)
result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10)
result[:wiki_pages] = Wiki.where(project_id: project_ids).search(query).limit(10)
result
end
......@@ -20,7 +21,8 @@ class SearchContext
@result ||= {
projects: [],
merge_requests: [],
issues: []
issues: [],
wiki_pages: []
}
end
end
......
class Admin::DashboardController < AdminController
def index
@workers = Resque.workers
@pending_jobs = Resque.size(:post_receive)
@projects = Project.order("created_at DESC").limit(10)
@users = User.order("created_at DESC").limit(10)
@resque_accessible = true
@workers = Resque.workers
@pending_jobs = Resque.size(:post_receive)
rescue Redis::InheritedError
@resque_accessible = false
end
end
......@@ -4,7 +4,7 @@ class Admin::ProjectsController < AdminController
def index
@admin_projects = Project.scoped
@admin_projects = @admin_projects.search(params[:name]) if params[:name].present?
@admin_projects = @admin_projects.page(params[:page]).per(20)
@admin_projects = @admin_projects.order("name ASC").page(params[:page]).per(20)
end
def show
......
......@@ -98,6 +98,9 @@ class Admin::UsersController < AdminController
def destroy
@admin_user = User.find(params[:id])
if @admin_user.my_own_projects.count > 0
redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return
end
@admin_user.destroy
respond_to do |format|
......
......@@ -9,19 +9,28 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can?
rescue_from Gitlab::Gitolite::AccessDenied do |exception|
log_exception(exception)
render "errors/gitolite", layout: "errors", status: 500
end
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
render "errors/encoding", layout: "errors", status: 500
end
rescue_from ActiveRecord::RecordNotFound do |exception|
log_exception(exception)
render "errors/not_found", layout: "errors", status: 404
end
protected
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
end
def reject_blocked!
if current_user && current_user.blocked
sign_out current_user
......
# Controller for viewing a file's blame
class BlobController < ProjectResourceController
include ExtractsPath
include Gitlab::Encode
# Authorize
before_filter :authorize_read_project!
......@@ -12,16 +11,9 @@ class BlobController < ProjectResourceController
def show
if @tree.is_blob?
if @tree.text?
encoding = detect_encoding(@tree.data)
mime_type = encoding ? "text/plain; charset=#{encoding}" : "text/plain"
else
mime_type = @tree.mime_type
end
send_data(
@tree.data,
type: mime_type,
type: @tree.mime_type,
disposition: 'inline',
filename: @tree.name
)
......
class DashboardController < ApplicationController
respond_to :html
before_filter :event_filter, only: :index
def index
@groups = Group.where(id: current_user.projects.pluck(:group_id))
@projects = current_user.projects_with_events
@projects = current_user.projects_sorted_by_activity
@projects = @projects.page(params[:page]).per(30)
@events = Event.in_projects(current_user.project_ids).limit(20).offset(params[:offset] || 0)
@events = Event.in_projects(current_user.project_ids)
@events = @event_filter.apply_filter(@events)
@events = @events.limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push
respond_to do |format|
......@@ -34,4 +39,8 @@ class DashboardController < ApplicationController
format.atom { render layout: false }
end
end
def event_filter
@event_filter ||= EventFilter.new(params[:event_filter])
end
end
......@@ -54,7 +54,7 @@ class GroupsController < ApplicationController
end
def projects
@projects ||= current_user.projects_with_events.where(group_id: @group.id)
@projects ||= current_user.projects_sorted_by_activity.where(group_id: @group.id)
end
def project_ids
......
......@@ -31,7 +31,8 @@ class MilestonesController < ProjectResourceController
def show
@issues = @milestone.issues
@users = @milestone.participants
@users = UserDecorator.decorate(@milestone.participants)
@merge_requests = @milestone.merge_requests
respond_to do |format|
format.html
......
......@@ -9,7 +9,11 @@ class ProfileController < ApplicationController
def update
@user.update_attributes(params[:user])
redirect_to :back
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
def token
......@@ -22,7 +26,7 @@ class ProfileController < ApplicationController
flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path
else
render action: "password"
render 'account'
end
end
......
require Rails.root.join('lib', 'gitlab', 'graph_commit')
require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')
class ProjectsController < ProjectResourceController
skip_before_filter :project, only: [:new, :create]
......@@ -21,9 +21,10 @@ class ProjectsController < ProjectResourceController
@project = Project.create_by_user(params[:project], current_user)
respond_to do |format|
flash[:notice] = 'Project was successfully created.' if @project.saved?
format.html do
if @project.saved?
redirect_to(@project, notice: 'Project was successfully created.')
redirect_to @project
else
render action: "new"
end
......@@ -79,7 +80,9 @@ class ProjectsController < ProjectResourceController
end
def graph
@days_json, @commits_json = Gitlab::GraphCommit.to_graph(project)
graph = Gitlab::Graph::JsonBuilder.new(project)
@days_json, @commits_json = graph.days_json, graph.commits_json
end
def destroy
......
class RefsController < ProjectResourceController
include Gitlab::Encode
# Authorize
before_filter :authorize_read_project!
......
......@@ -16,6 +16,11 @@ class RepositoriesController < ProjectResourceController
@tags = @project.tags
end
def stats
@stats = Gitlab::GitStats.new(@project.repo, @project.root_ref)
@graph = @stats.graph
end
def archive
unless can?(current_user, :download_code, @project)
render_404 and return
......
......@@ -5,5 +5,6 @@ class SearchController < ApplicationController
@projects = result[:projects]
@merge_requests = result[:merge_requests]
@issues = result[:issues]
@wiki_pages = result[:wiki_pages]
end
end
class ServicesController < ProjectResourceController
# Authorize
before_filter :authorize_admin_project!
respond_to :html
def index
@gitlab_ci_service = @project.gitlab_ci_service
end
def edit
@service = @project.gitlab_ci_service
# Create if missing
@service = @project.create_gitlab_ci_service unless @service
end
def update
@service = @project.gitlab_ci_service
if @service.update_attributes(params[:service])
redirect_to edit_project_service_path(@project, :gitlab_ci)
else
render 'edit'
end
end
def test
commits = project.commits(project.default_branch, nil, 3)
data = project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}", current_user)
@service = project.gitlab_ci_service
@service.execute(data)
redirect_to :back
end
end
......@@ -26,15 +26,14 @@ class TreeController < ProjectResourceController
end
def update
file_editor = Gitlab::FileEditor.new(current_user, @project, @ref)
update_status = file_editor.update(
@path,
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
updated_successfully = edit_file_action.commit!(
params[:content],
params[:commit_message],
params[:last_commit]
)
if update_status
if updated_successfully
redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
else
flash[:notice] = "Your changes could not be commited, because the file has been changed"
......
......@@ -47,21 +47,15 @@ class CommitDecorator < ApplicationDecorator
# Otherwise it will link to the author email as specified in the commit.
#
# options:
# avatar: true will prepend avatar image
def author_link(options)
text = if options[:avatar]
avatar = h.image_tag h.gravatar_icon(author_email), class: "avatar", width: 16
"#{avatar} #{author_name}"
else
author_name
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def author_link(options = {})
person_link(options.merge source: :author)
end
team_member = @project.try(:team_member_by_name_or_email, author_name, author_email)
if team_member.nil?
h.mail_to author_email, text.html_safe, class: "commit-author-link"
else
h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-author-link"
end
# Just like #author_link but for the committer.
def committer_link(options = {})
person_link(options.merge source: :committer)
end
protected
......@@ -69,4 +63,30 @@ class CommitDecorator < ApplicationDecorator
def no_commit_message
"--no commit message"
end
# Private: Returns a link to a person. If the person has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the person email as specified in the commit.
#
# options:
# source: one of :author or :committer
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def person_link(options = {})
source_name = send "#{options[:source]}_name".to_sym
source_email = send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size]
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
end
team_member = @project.try(:team_member_by_name_or_email, source_name, source_email)
if team_member.nil?
h.mail_to source_email, text.html_safe, class: "commit-#{options[:source]}-link"
else
h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-#{options[:source]}-link"
end
end
end
......@@ -8,14 +8,14 @@ class TreeDecorator < ApplicationDecorator
#parts = parts[0...-1] if is_blob?
yield(h.link_to("..", "#", remote: true)) if parts.count > max_links
yield(h.link_to("..", "#")) if parts.count > max_links
parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links
yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)), remote: true))
yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path))))
end
end
end
......
class UserDecorator < ApplicationDecorator
decorates :user
def avatar_image size = 16
h.image_tag h.gravatar_icon(self.email, size), class: "avatar #{"s#{size}"}", width: size
end
def tm_of(project)
project.team_member_by_id(self.id)
end
end
......@@ -36,7 +36,7 @@ module ApplicationHelper
else
gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
user_email.strip!
"#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon"
"#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm"
end
end
......
......@@ -57,12 +57,17 @@ module CommitsHelper
def image_diff_class(diff)
if diff.deleted_file
"diff_image_removed"
"diff_removed"
elsif diff.new_file
"diff_image_added"
"diff_added"
else
nil
end
end
def commit_to_html commit
if commit.model
escape_javascript(render 'commits/commit', commit: commit)
end
end
end
......@@ -33,4 +33,22 @@ module EventsHelper
image_tag event_image_path
end
end
def event_filter_link key, tooltip
key = key.to_s
filter = @event_filter.options key
inactive = if @event_filter.active? key
nil
else
'inactive'
end
content_tag :div, class: "filter_icon #{inactive}" do
link_to dashboard_path(event_filter: filter), class: 'has_tooltip', 'data-original-title' => tooltip do
image_tag "event_filter_#{key}.png"
end
end
end
end
......@@ -38,4 +38,8 @@ module MergeRequestsHelper
classes << " merged" if mr.merged?
classes
end
def ci_status_path
@project.gitlab_ci_service.commit_badge_path(@merge_request.last_commit.sha)
end
end
......@@ -10,5 +10,9 @@ module ProjectsHelper
def link_to_project project
link_to project.name, project
end
def tm_path team_member
project_team_member_path(@project, team_member)
end
end
......@@ -67,4 +67,29 @@ module TreeHelper
can?(current_user, :push_code, @project)
end
end
# Breadcrumb links for a Project and, if applicable, a tree path
def breadcrumbs
return unless @project && @ref
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do
content_tag(:span, nil, class: 'arrow') +
link_to(@project.name, project_commits_path(@project, @ref))
end
if @path
parts = @path.split('/')
parts.each_with_index do |part, i|
crumbs += content_tag(:span, '/', class: 'divider')
crumbs += content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
end
end
crumbs.html_safe
end
end
class Commit
include ActiveModel::Conversion
include Gitlab::Encode
include StaticModel
extend ActiveModel::Naming
# Safe amount of files with diffs in one commit to render
# Used to prevent 500 error on huge commits by suppressing diff
#
DIFF_SAFE_SIZE = 100
attr_accessor :commit, :head, :refs
delegate :message, :authored_date, :committed_date, :parents, :sha,
......@@ -107,7 +111,7 @@ class Commit
end
def safe_message
@safe_message ||= utf8 message
@safe_message ||= message
end
def created_at
......@@ -119,7 +123,7 @@ class Commit
end
def author_name
utf8 author.name
author.name
end
# Was this commit committed by a different person than the original author?
......@@ -128,7 +132,7 @@ class Commit
end
def committer_name
utf8 committer.name
committer.name
end
def committer_email
......
# == Schema Information
#
# Table name: events
#
# id :integer not null, primary key
# target_type :string(255)
# target_id :integer
# title :string(255)
# data :text
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# action :integer
# author_id :integer
#
class Event < ActiveRecord::Base
include PushEvent
......@@ -144,20 +160,3 @@ class Event < ActiveRecord::Base
end
end
end
# == Schema Information
#
# Table name: events
#
# id :integer not null, primary key
# target_type :string(255)
# target_id :integer
# title :string(255)
# data :text
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# action :integer
# author_id :integer
#
# == Schema Information
#
# Table name: services
#
# id :integer not null, primary key
# type :string(255)
# title :string(255)
# token :string(255)
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# active :boolean default(FALSE), not null
# project_url :string(255)
#
class GitlabCiService < Service
attr_accessible :project_url
validates :project_url, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
delegate :execute, to: :service_hook, prefix: nil
after_save :compose_service_hook, if: :activated?
def activated?
active
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = [project_url, "/build", "?token=#{token}"].join("")
hook.save
end
def commit_badge_path sha
project_url + "/status?sha=#{sha}"
end
end
# == Schema Information
#
# Table name: groups
#
# id :integer not null, primary key
# name :string(255) not null
# code :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Group < ActiveRecord::Base
attr_accessible :code, :name, :owner_id
......@@ -22,16 +34,3 @@ class Group < ActiveRecord::Base
User.joins(:users_projects).where(users_projects: {project_id: project_ids}).uniq
end
end
# == Schema Information
#
# Table name: groups
#
# id :integer not null, primary key
# name :string(255) not null
# code :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Issue < ActiveRecord::Base
include IssueCommonality
include Votes
attr_accessible :title, :assignee_id, :closed, :position, :description,
:milestone_id, :label_list, :author_id_of_changes
acts_as_taggable_on :labels
belongs_to :milestone
validates :description, length: { within: 0..2000 }
def self.open_for(user)
opened.assigned(user)
end
end
# == Schema Information
#
# Table name: issues
......@@ -34,3 +16,18 @@ end
# milestone_id :integer
#
class Issue < ActiveRecord::Base
include IssueCommonality
include Votes
attr_accessible :title, :assignee_id, :closed, :position, :description,
:milestone_id, :label_list, :author_id_of_changes
acts_as_taggable_on :labels
validates :description, length: { within: 0..2000 }
def self.open_for(user)
opened.assigned(user)
end
end
# == Schema Information
#
# Table name: keys
#
# id :integer not null, primary key
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# key :text
# title :string(255)
# identifier :string(255)
# project_id :integer
#
require 'digest/md5'
class Key < ActiveRecord::Base
......@@ -67,18 +81,3 @@ class Key < ActiveRecord::Base
Key.where(identifier: identifier).count == 0
end
end
# == Schema Information
#
# Table name: keys
#
# id :integer not null, primary key
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# key :text
# title :string(255)
# identifier :string(255)
# project_id :integer
#
# == Schema Information
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# closed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# st_commits :text(2147483647)
# st_diffs :text(2147483647)
# merged :boolean default(FALSE), not null
# state :integer default(1), not null
# milestone_id :integer
#
require Rails.root.join("app/models/commit")
require Rails.root.join("app/roles/static_model")
class MergeRequest < ActiveRecord::Base
include IssueCommonality
include Votes
attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch,
attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,
:author_id_of_changes
attr_accessor :should_remove_source_branch
......@@ -26,6 +48,10 @@ class MergeRequest < ActiveRecord::Base
where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
end
def self.find_all_by_milestone(milestone)
where("milestone_id = :milestone_id", milestone_id: milestone)
end
def human_state
states = {
CAN_BE_MERGED => "can_be_merged",
......@@ -60,7 +86,7 @@ class MergeRequest < ActiveRecord::Base
end
def check_if_can_be_merged
self.state = if Gitlab::Merge.new(self, self.author).can_be_merged?
self.state = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
CAN_BE_MERGED
else
CANNOT_BE_MERGED
......@@ -167,7 +193,7 @@ class MergeRequest < ActiveRecord::Base
end
def automerge!(current_user)
if Gitlab::Merge.new(self, current_user).merge! && self.unmerged_commits.empty?
if Gitlab::Satellite::MergeAction.new(current_user, self).merge! && self.unmerged_commits.empty?
self.merge!(current_user.id)
true
end
......@@ -193,24 +219,3 @@ class MergeRequest < ActiveRecord::Base
Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND noteable_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
end
end
# == Schema Information
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# closed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# st_commits :text(4294967295
# st_diffs :text(4294967295
# merged :boolean default(FALSE), not null
# state :integer default(1), not null
#
# == Schema Information
#
# Table name: milestones
#
# id :integer not null, primary key
# title :string(255) not null
# project_id :integer not null
# description :text
# due_date :date
# closed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Milestone < ActiveRecord::Base
attr_accessible :title, :description, :due_date, :closed
belongs_to :project
has_many :issues
has_many :merge_requests
validates :title, presence: true
validates :project, presence: true
validates :closed, inclusion: { in: [true, false] }
def self.active
where("due_date > ? OR due_date IS NULL", Date.today)
......@@ -15,8 +31,20 @@ class Milestone < ActiveRecord::Base
User.where(id: issues.pluck(:assignee_id))
end
def open_items_count
self.issues.opened.count + self.merge_requests.opened.count
end
def closed_items_count
self.issues.closed.count + self.merge_requests.closed.count
end
def total_items_count
self.issues.count + self.merge_requests.count
end
def percent_complete
((self.issues.closed.count * 100) / self.issues.count).abs
((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError
100
end
......@@ -25,18 +53,3 @@ class Milestone < ActiveRecord::Base
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
end
end
# == Schema Information
#
# Table name: milestones
#
# id :integer not null, primary key
# title :string(255) not null
# project_id :integer not null
# description :text
# due_date :date
# closed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
# == Schema Information
#
# Table name: notes
#
# id :integer not null, primary key
# note :text
# noteable_id :string(255)
# noteable_type :string(255)
# author_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# project_id :integer
# attachment :string(255)
# line_code :string(255)
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
......@@ -23,13 +39,13 @@ class Note < ActiveRecord::Base
mount_uploader :attachment, AttachmentUploader
# Scopes
scope :common, where(noteable_id: nil)
scope :today, where("created_at >= :date", date: Date.today)
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
scope :common, ->{ where(noteable_id: nil) }
scope :today, ->{ where("created_at >= :date", date: Date.today) }
scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
scope :since, ->(day) { where("created_at >= :date", date: (day)) }
scope :fresh, order("created_at ASC, id ASC")
scope :inc_author_project, includes(:project, :author)
scope :inc_author, includes(:author)
scope :fresh, ->{ order("created_at ASC, id ASC") }
scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) }
def self.create_status_change_note(noteable, author, status)
create({
......@@ -107,20 +123,3 @@ class Note < ActiveRecord::Base
note.start_with?('-1') || note.start_with?(':-1:')
end
end
# == Schema Information
#
# Table name: notes
#
# id :integer not null, primary key
# note :text
# noteable_id :string(255)
# noteable_type :string(255)
# author_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# project_id :integer
# attachment :string(255)
# line_code :string(255)
#
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
# created_at :datetime not null
# updated_at :datetime not null
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
# group_id :integer
#
require "grit"
class Project < ActiveRecord::Base
......@@ -26,6 +47,7 @@ class Project < ActiveRecord::Base
has_many :wikis, dependent: :destroy
has_many :protected_branches, dependent: :destroy
has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
has_one :gitlab_ci_service, dependent: :destroy
delegate :name, to: :owner, allow_nil: true, prefix: true
......@@ -104,8 +126,10 @@ class Project < ActiveRecord::Base
end
def repo_name
if path == "gitolite-admin"
errors.add(:path, " like 'gitolite-admin' is not allowed")
denied_paths = %w(gitolite-admin groups projects dashboard)
if denied_paths.include?(path)
errors.add(:path, "like #{path} is not allowed")
end
end
......@@ -160,26 +184,12 @@ class Project < ActiveRecord::Base
def issues_labels
issues.tag_counts_on(:labels)
end
end
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
# created_at :datetime not null
# updated_at :datetime not null
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
# group_id :integer
#
def services
[gitlab_ci_service].compact
end
def gitlab_ci?
gitlab_ci_service && gitlab_ci_service.active
end
end
class ProjectHook < WebHook
belongs_to :project
end
# == Schema Information
#
# Table name: web_hooks
......@@ -12,5 +8,9 @@ end
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255) default("ProjectHook")
# service_id :integer
#
class ProjectHook < WebHook
belongs_to :project
end
# == Schema Information
#
# Table name: protected_branches
#
# id :integer not null, primary key
# project_id :integer not null
# name :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
#
class ProtectedBranch < ActiveRecord::Base
include GitHost
......@@ -18,15 +29,3 @@ class ProtectedBranch < ActiveRecord::Base
project.commit(self.name)
end
end
# == Schema Information
#
# Table name: protected_branches
#
# id :integer not null, primary key
# project_id :integer not null
# name :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
#
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment