Commit d1564370 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' into stable

parents fa1f8513 da854542

Too many changes to show.

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

env: env:
- DB=postgresql
- DB=mysql - DB=mysql
before_install: before_install:
- sudo apt-get install libicu-dev -y - sudo apt-get install libicu-dev -y
...@@ -11,6 +12,7 @@ rvm: ...@@ -11,6 +12,7 @@ rvm:
- 1.9.3 - 1.9.3
services: services:
- mysql - mysql
- postgresql
before_script: before_script:
- "cp config/database.yml.$DB config/database.yml" - "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml" - "cp config/gitlab.yml.example config/gitlab.yml"
...@@ -18,4 +20,4 @@ before_script: ...@@ -18,4 +20,4 @@ before_script:
- "bundle exec rake db:migrate RAILS_ENV=test" - "bundle exec rake db:migrate RAILS_ENV=test"
- "bundle exec rake db:seed_fu RAILS_ENV=test" - "bundle exec rake db:seed_fu RAILS_ENV=test"
- "sh -e /etc/init.d/xvfb start" - "sh -e /etc/init.d/xvfb start"
script: "bundle exec rake travis" script: "bundle exec rake travis --trace"
v 3.0.0
- Projects groups
- Web Editor
- Fixed bug with gitolite keys
- UI improved
- Increased perfomance of application
- Show user avatar in last commit when browsing Files
- Refactored Gitlab::Merge
- Use Font Awsome for icons
- Separate observing of Note and MergeRequestsa
- Milestone "All Issues" filter
- Fix issue close and reopen button text and styles
- Fix forward/back while browsing Tree hierarchy
- Show numer of notes for commits and merge requests
- Added support pg from box and update installation doc
- Reject ssh keys that break gitolite
- [API] list one project hook
- [API] edit project hook
- [API] add project snippets list
- [API] allow to authorize using private token in HTTP header
- [API] add user creation
v 2.9.1 v 2.9.1
- Fixed resque custom config init - Fixed resque custom config init
......
...@@ -7,12 +7,12 @@ If you want to contribute to GitLab, follow this process: ...@@ -7,12 +7,12 @@ If you want to contribute to GitLab, follow this process:
3. Code 3. Code
4. Create a pull request 4. Create a pull request
We only accept pull requests if: We will only accept pull requests if:
* Your code has proper tests and all tests pass * Your code has proper tests and all tests pass
* Your code can be merged w/o problems * Your code can be merged w/o problems
* It wont broke existing functionality * It won't break existing functionality
* Its a quality code * It's quality code
* We like it :) * We like it :)
## [You may need a developer VM](https://github.com/gitlabhq/developer-vm) ## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
......
...@@ -11,8 +11,9 @@ end ...@@ -11,8 +11,9 @@ end
gem "rails", "3.2.8" gem "rails", "3.2.8"
# Supported DBs # Supported DBs
gem "sqlite3" gem "sqlite3", :group => :sqlite
gem "mysql2" gem "mysql2", :group => :mysql
gem "pg", :group => :postgres
# Auth # Auth
gem "devise", "~> 2.1.0" gem "devise", "~> 2.1.0"
...@@ -23,12 +24,18 @@ gem 'omniauth-github' ...@@ -23,12 +24,18 @@ gem 'omniauth-github'
# GITLAB patched libs # GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
gem "gitolite", :git => "https://github.com/gitlabhq/gitolite-client.git", :ref => "9b715ca8bab6529f6c92204a25f84d12f25a6eb0"
gem "pygments.rb", :git => "https://github.com/gitlabhq/pygments.rb.git", :ref => "2cada028da5054616634a1d9ca6941b65b3ce188"
gem "omniauth-ldap", :git => "https://github.com/gitlabhq/omniauth-ldap.git", :ref => "f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e" 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 'yaml_db', :git => "https://github.com/gitlabhq/yaml_db.git"
gem 'grack', :git => "https://github.com/gitlabhq/grack.git" gem 'grack', :git => "https://github.com/gitlabhq/grack.git"
gem "linguist", "~> 1.0.0", :git => "https://github.com/gitlabhq/linguist.git"
# Gitolite client (for work with gitolite-admin repo)
gem "gitolite", '1.1.0'
# Syntax highlighter
gem "pygments.rb", "0.3.1"
# Language detection
gem "github-linguist", "~> 2.3.4" , :require => "linguist"
# API # API
gem "grape", "~> 0.2.1" gem "grape", "~> 0.2.1"
...@@ -57,7 +64,7 @@ gem "seed-fu" ...@@ -57,7 +64,7 @@ gem "seed-fu"
# Markdown to HTML # Markdown to HTML
gem "redcarpet", "~> 2.1.1" gem "redcarpet", "~> 2.1.1"
gem "github-markup", "~> 0.7.4" gem "github-markup", "~> 0.7.4", require: 'github/markup'
# Servers # Servers
gem "thin" gem "thin"
...@@ -87,6 +94,7 @@ gem 'settingslogic' ...@@ -87,6 +94,7 @@ gem 'settingslogic'
# Misc # Misc
gem "foreman" gem "foreman"
gem 'gemoji', require: 'emoji/railtie'
gem "git" gem "git"
group :assets do group :assets do
...@@ -96,11 +104,13 @@ group :assets do ...@@ -96,11 +104,13 @@ group :assets do
gem "therubyracer" gem "therubyracer"
gem 'chosen-rails' gem 'chosen-rails'
gem 'jquery-atwho-rails', '0.1.6'
gem "jquery-rails", "2.0.2" gem "jquery-rails", "2.0.2"
gem "jquery-ui-rails", "0.5.0" gem "jquery-ui-rails", "0.5.0"
gem "modernizr", "2.5.3" gem "modernizr", "2.5.3"
gem "raphael-rails", "1.5.2" gem "raphael-rails", "1.5.2"
gem 'bootstrap-sass', "2.0.4" gem 'bootstrap-sass', "2.0.4"
gem "font-awesome-sass-rails", "~> 2.0.0"
end end
group :development do group :development do
...@@ -110,6 +120,7 @@ group :development do ...@@ -110,6 +120,7 @@ group :development do
end end
group :development, :test do group :development, :test do
gem 'rails-dev-tweaks'
gem 'spinach-rails' gem 'spinach-rails'
gem "rspec-rails" gem "rspec-rails"
gem "capybara" gem "capybara"
...@@ -137,8 +148,9 @@ group :test do ...@@ -137,8 +148,9 @@ group :test do
gem 'email_spec' gem 'email_spec'
gem 'resque_spec' gem 'resque_spec'
gem "webmock" gem "webmock"
gem 'test_after_commit'
end end
group :production do group :production do
gem "gitlab_meta", '2.9' gem "gitlab_meta", '3.0'
end end
...@@ -4,15 +4,6 @@ GIT ...@@ -4,15 +4,6 @@ GIT
specs: specs:
annotate (2.4.1.beta1) annotate (2.4.1.beta1)
GIT
remote: https://github.com/gitlabhq/gitolite-client.git
revision: 9b715ca8bab6529f6c92204a25f84d12f25a6eb0
ref: 9b715ca8bab6529f6c92204a25f84d12f25a6eb0
specs:
gitolite (0.0.4.alpha)
grit (>= 2.4.1)
hashery (~> 1.4.0)
GIT GIT
remote: https://github.com/gitlabhq/grack.git remote: https://github.com/gitlabhq/grack.git
revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8 revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
...@@ -30,16 +21,6 @@ GIT ...@@ -30,16 +21,6 @@ GIT
mime-types (~> 1.15) mime-types (~> 1.15)
posix-spawn (~> 0.3.6) posix-spawn (~> 0.3.6)
GIT
remote: https://github.com/gitlabhq/linguist.git
revision: c3d6fc5af8cf9d67afa572bba363bf0db256a900
specs:
linguist (1.0.0)
charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.3)
mime-types (~> 1.18)
pygments.rb (~> 0.2.11)
GIT GIT
remote: https://github.com/gitlabhq/omniauth-ldap.git remote: https://github.com/gitlabhq/omniauth-ldap.git
revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
...@@ -51,14 +32,6 @@ GIT ...@@ -51,14 +32,6 @@ GIT
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1) rubyntlm (~> 0.1.1)
GIT
remote: https://github.com/gitlabhq/pygments.rb.git
revision: 2cada028da5054616634a1d9ca6941b65b3ce188
ref: 2cada028da5054616634a1d9ca6941b65b3ce188
specs:
pygments.rb (0.2.13)
rubypython (~> 0.6.1)
GIT GIT
remote: https://github.com/gitlabhq/yaml_db.git remote: https://github.com/gitlabhq/yaml_db.git
revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd
...@@ -162,18 +135,32 @@ GEM ...@@ -162,18 +135,32 @@ GEM
multipart-post (~> 1.1) multipart-post (~> 1.1)
ffaker (1.14.0) ffaker (1.14.0)
ffi (1.0.11) ffi (1.0.11)
font-awesome-sass-rails (2.0.0.0)
railties (>= 3.1.1)
sass-rails (>= 3.1.1)
foreman (0.47.0) foreman (0.47.0)
thor (>= 0.13.6) thor (>= 0.13.6)
gemoji (1.1.1)
gherkin-ruby (0.2.1) gherkin-ruby (0.2.1)
git (1.2.5) git (1.2.5)
github-linguist (2.3.4)
charlock_holmes (~> 0.6.6)
escape_utils (~> 0.2.3)
mime-types (~> 1.19)
pygments.rb (>= 0.2.13)
github-markup (0.7.4) github-markup (0.7.4)
gitlab_meta (2.9) gitlab_meta (3.0)
gitolite (1.1.0)
gratr19 (~> 0.4.4.1)
grit (~> 2.5.0)
hashery (~> 1.5.0)
grape (0.2.1) grape (0.2.1)
hashie (~> 1.2) hashie (~> 1.2)
multi_json multi_json
multi_xml multi_xml
rack rack
rack-mount rack-mount
gratr19 (0.4.4.1)
growl (1.0.3) growl (1.0.3)
guard (1.3.2) guard (1.3.2)
listen (>= 0.4.2) listen (>= 0.4.2)
...@@ -189,7 +176,8 @@ GEM ...@@ -189,7 +176,8 @@ GEM
activesupport (~> 3.0) activesupport (~> 3.0)
haml (~> 3.0) haml (~> 3.0)
railties (~> 3.0) railties (~> 3.0)
hashery (1.4.0) hashery (1.5.0)
blankslate
hashie (1.2.0) hashie (1.2.0)
headless (0.3.1) headless (0.3.1)
hike (1.2.1) hike (1.2.1)
...@@ -199,6 +187,7 @@ GEM ...@@ -199,6 +187,7 @@ GEM
httpauth (0.1) httpauth (0.1)
i18n (0.6.1) i18n (0.6.1)
journey (1.0.4) journey (1.0.4)
jquery-atwho-rails (0.1.6)
jquery-rails (2.0.2) jquery-rails (2.0.2)
railties (>= 3.2.0, < 5.0) railties (>= 3.2.0, < 5.0)
thor (~> 0.14) thor (~> 0.14)
...@@ -260,12 +249,16 @@ GEM ...@@ -260,12 +249,16 @@ GEM
multi_json (~> 1.3) multi_json (~> 1.3)
omniauth-oauth (~> 1.0) omniauth-oauth (~> 1.0)
orm_adapter (0.3.0) orm_adapter (0.3.0)
pg (0.14.0)
polyglot (0.3.3) polyglot (0.3.3)
posix-spawn (0.3.6) posix-spawn (0.3.6)
pry (0.9.9.6) pry (0.9.9.6)
coderay (~> 1.0.5) coderay (~> 1.0.5)
method_source (~> 0.7.1) method_source (~> 0.7.1)
slop (>= 2.4.4, < 3) slop (>= 2.4.4, < 3)
pygments.rb (0.3.1)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
rack (1.4.1) rack (1.4.1)
rack-cache (1.2) rack-cache (1.2)
...@@ -288,6 +281,9 @@ GEM ...@@ -288,6 +281,9 @@ GEM
activesupport (= 3.2.8) activesupport (= 3.2.8)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.2.8) railties (= 3.2.8)
rails-dev-tweaks (0.6.1)
actionpack (~> 3.1)
railties (~> 3.1)
railties (3.2.8) railties (3.2.8)
actionpack (= 3.2.8) actionpack (= 3.2.8)
activesupport (= 3.2.8) activesupport (= 3.2.8)
...@@ -332,9 +328,6 @@ GEM ...@@ -332,9 +328,6 @@ GEM
railties (>= 3.0) railties (>= 3.0)
rspec (~> 2.10.0) rspec (~> 2.10.0)
rubyntlm (0.1.1) rubyntlm (0.1.1)
rubypython (0.6.2)
blankslate (>= 2.1.2.3)
ffi (~> 1.0.7)
rubyzip (0.9.8) rubyzip (0.9.8)
sass (3.1.19) sass (3.1.19)
sass-rails (3.2.5) sass-rails (3.2.5)
...@@ -376,6 +369,7 @@ GEM ...@@ -376,6 +369,7 @@ GEM
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.6) sqlite3 (1.3.6)
stamp (0.1.6) stamp (0.1.6)
test_after_commit (0.0.1)
therubyracer (0.10.1) therubyracer (0.10.1)
libv8 (~> 3.3.10) libv8 (~> 3.3.10)
thin (1.3.1) thin (1.3.1)
...@@ -404,6 +398,7 @@ GEM ...@@ -404,6 +398,7 @@ GEM
crack (>= 0.1.7) crack (>= 0.1.7)
xpath (0.1.4) xpath (0.1.4)
nokogiri (~> 1.3) nokogiri (~> 1.3)
yajl-ruby (1.1.0)
PLATFORMS PLATFORMS
ruby ruby
...@@ -426,11 +421,14 @@ DEPENDENCIES ...@@ -426,11 +421,14 @@ DEPENDENCIES
email_spec email_spec
factory_girl_rails factory_girl_rails
ffaker ffaker
font-awesome-sass-rails (~> 2.0.0)
foreman foreman
gemoji
git git
github-linguist (~> 2.3.4)
github-markup (~> 0.7.4) github-markup (~> 0.7.4)
gitlab_meta (= 2.9) gitlab_meta (= 3.0)
gitolite! gitolite (= 1.1.0)
grack! grack!
grape (~> 0.2.1) grape (~> 0.2.1)
grit! grit!
...@@ -440,12 +438,12 @@ DEPENDENCIES ...@@ -440,12 +438,12 @@ DEPENDENCIES
haml-rails haml-rails
headless headless
httparty httparty
jquery-atwho-rails (= 0.1.6)
jquery-rails (= 2.0.2) jquery-rails (= 2.0.2)
jquery-ui-rails (= 0.5.0) jquery-ui-rails (= 0.5.0)
kaminari kaminari
launchy launchy
letter_opener letter_opener
linguist (~> 1.0.0)!
modernizr (= 2.5.3) modernizr (= 2.5.3)
mysql2 mysql2
omniauth omniauth
...@@ -453,10 +451,12 @@ DEPENDENCIES ...@@ -453,10 +451,12 @@ DEPENDENCIES
omniauth-google-oauth2 omniauth-google-oauth2
omniauth-ldap! omniauth-ldap!
omniauth-twitter omniauth-twitter
pg
pry pry
pygments.rb! pygments.rb (= 0.3.1)
rack-mini-profiler rack-mini-profiler
rails (= 3.2.8) rails (= 3.2.8)
rails-dev-tweaks
raphael-rails (= 1.5.2) raphael-rails (= 1.5.2)
rb-fsevent rb-fsevent
rb-inotify rb-inotify
...@@ -474,6 +474,7 @@ DEPENDENCIES ...@@ -474,6 +474,7 @@ DEPENDENCIES
spinach-rails spinach-rails
sqlite3 sqlite3
stamp stamp
test_after_commit
therubyracer therubyracer
thin thin
uglifier (= 1.0.3) uglifier (= 1.0.3)
......
...@@ -12,7 +12,7 @@ GitLab is a free project and repository management application ...@@ -12,7 +12,7 @@ GitLab is a free project and repository management application
## Requirements ## Requirements
* Ubuntu/Debian * Ubuntu/Debian
* ruby 1.9.2+ * ruby 1.9.3+
* mysql or sqlite * mysql or sqlite
* git * git
* gitolite * gitolite
......
...@@ -10,10 +10,13 @@ ...@@ -10,10 +10,13 @@
//= require jquery.cookie //= require jquery.cookie
//= require jquery.endless-scroll //= require jquery.endless-scroll
//= require jquery.highlight //= require jquery.highlight
//= require jquery.history
//= require jquery.waitforimages //= require jquery.waitforimages
//= require jquery.atwho
//= require bootstrap //= require bootstrap
//= require modernizr //= require modernizr
//= require chosen-jquery //= require chosen-jquery
//= require raphael //= require raphael
//= require branch-graph //= require branch-graph
//= require ace-src-noconflict/ace
//= require_tree . //= require_tree .
###
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>";
# 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) ->
(getMoreMembers = ->
$.getJSON(autocompleteMembersUrl, autocompleteMembersParams)
.success (members) ->
# pick the data we need
newMembersData = $.map members, (m) -> m.name
# add the new page of data to the rest
$.merge autocompleteMembersData, newMembersData
# show the pop-up with a copy of the current data
callback autocompleteMembersData[..]
# are we past the last page?
if newMembersData.length == 0
# set static data and stop callbacks
$('.gfm-input').atWho '@',
data: autocompleteMembersData
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
...@@ -6,6 +6,7 @@ function switchToNewIssue(form){ ...@@ -6,6 +6,7 @@ function switchToNewIssue(form){
$("#new_issue_dialog").show("fade", { direction: "right" }, 150); $("#new_issue_dialog").show("fade", { direction: "right" }, 150);
$('.top-tabs .add_new').hide(); $('.top-tabs .add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn"); disableButtonIfEmptyField("#issue_title", ".save-btn");
setupGfmAutoComplete();
}); });
} }
...@@ -17,6 +18,7 @@ function switchToEditIssue(form){ ...@@ -17,6 +18,7 @@ function switchToEditIssue(form){
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150); $("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
$('.add_new').hide(); $('.add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn"); disableButtonIfEmptyField("#issue_title", ".save-btn");
setupGfmAutoComplete();
}); });
} }
......
$ ->
$('.milestone-issue-filter tr[data-closed]').addClass('hide')
$('.milestone-issue-filter ul.nav li a').click ->
$('.milestone-issue-filter li').toggleClass('active')
$('.milestone-issue-filter tr[data-closed]').toggleClass('hide')
false
...@@ -230,7 +230,7 @@ var NoteList = { ...@@ -230,7 +230,7 @@ var NoteList = {
updateVotes: updateVotes:
function() { function() {
var votes = $("#votes .votes"); var votes = $("#votes .votes");
var notes = $("#notes-list, #new-notes-list").find(".note.vote"); var notes = $("#notes-list, #new-notes-list").find(".note .vote");
// only update if there is a vote display // only update if there is a vote display
if (votes.size()) { if (votes.size()) {
......
/**
* Tree slider for code browse
*
*/
var Tree = {
init:
function() {
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live("click", function() {
$("#tree-content-holder").hide("slide", { direction: "left" }, 150)
})
$('.project-refs-form').live({
"ajax:beforeSend": function() {
$("#tree-content-holder").hide("slide", { direction: "left" }, 150);
}
})
$("#tree-slider .tree-item").live('click', function(e){
if(e.target.nodeName != "A") {
link = $(this).find(".tree-item-file-name a");
link.trigger("click");
}
});
$('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live({
"ajax:beforeSend": function() { $('.tree_progress').addClass("loading"); },
"ajax:complete": function() { $('.tree_progress').removeClass("loading"); }
});
}
}
# Code browser tree slider
$ ->
if $('#tree-slider').length > 0
# Show the "Loading commit data" for only the first element
$('span.log_loading:first').removeClass('hide')
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", ->
$("#tree-content-holder").hide("slide", { direction: "left" }, 150)
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
$("#tree-slider .tree-item").live 'click', (e) ->
$('.tree-item-file-name a', this).trigger('click') if (e.target.nodeName != "A")
# Show/Hide the loading spinner
$('#tree-slider .tree-item-file-name a, .breadcrumb a, .project-refs-form').live
"ajax:beforeSend": -> $('.tree_progress').addClass("loading")
"ajax:complete": -> $('.tree_progress').removeClass("loading")
# Maintain forward/back history while browsing the file tree
((window) ->
History = window.History
$ = window.jQuery
document = window.document
# Check to see if History.js is enabled for our Browser
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
History.Adapter.bind window, 'statechange', ->
state = History.getState()
window.ajaxGet(state.url)
)(window)
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* the top of the compiled file, but it's generally better to create a new file per style scope. * the top of the compiled file, but it's generally better to create a new file per style scope.
*= require jquery.ui.all *= require jquery.ui.all
*= require jquery.ui.aristo *= require jquery.ui.aristo
*= require jquery.atwho
*= require chosen *= require chosen
*= require_self *= require_self
*= require main *= require main
......
...@@ -185,36 +185,6 @@ span.update-author { ...@@ -185,36 +185,6 @@ span.update-author {
} }
} }
.event_label {
@extend .label;
background-color: #999;
&.pushed {
background-color: #4A97BD;
}
&.opened {
background-color: #469847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
&.joined {
background-color: #1ca9dd;
}
&.left {
background-color: #888;
float:none;
}
}
form { form {
@extend .form-horizontal; @extend .form-horizontal;
...@@ -355,41 +325,6 @@ p.time { ...@@ -355,41 +325,6 @@ p.time {
border:2px solid #ddd; border:2px solid #ddd;
} }
.event_feed {
min-height:40px;
border-bottom:1px solid #ddd;
.avatar {
width:32px;
}
.event_icon {
float:right;
margin-right:2px;
img {
width:20px;
}
}
ul {
margin-left:50px;
margin-bottom:5px;
.avatar {
width:24px;
}
}
padding: 15px 5px;
&:last-child { border:none }
.wll:hover { background:none }
.event_commits {
margin-top: 5px;
li.commit {
background: transparent;
padding:5px;
border:none;
}
}
}
.ico { .ico {
background: url("images.png") no-repeat -85px -77px; background: url("images.png") no-repeat -85px -77px;
...@@ -639,22 +574,6 @@ li.note { ...@@ -639,22 +574,6 @@ li.note {
background:#fff; background:#fff;
} }
/**
* Push event widget
*
*/
.event_lp {
@extend .ui-box;
color:#777;
margin-bottom:20px;
padding:8px;
@include border-radius(4px);
min-height:22px;
.avatar {
width:24px;
}
}
.supp_diff_link, .supp_diff_link,
.mr_show_all_commits { .mr_show_all_commits {
...@@ -742,3 +661,12 @@ li.note { ...@@ -742,3 +661,12 @@ li.note {
margin-right: 30px; margin-right: 30px;
} }
} }
pre {
&.clean {
background:none;
border:none;
margin:0;
padding:0;
}
}
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
color:#888; color:#888;
text-shadow:0 1px 1px #fff; text-shadow:0 1px 1px #fff;
} }
i[class^="icon-"] {
line-height:14px;
}
} }
&.active { &.active {
> a { > a {
......
...@@ -68,10 +68,22 @@ ...@@ -68,10 +68,22 @@
* Blame file * Blame file
*/ */
&.blame { &.blame {
table {
border:none;
box-shadow:none;
margin:0;
}
tr { tr {
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
td { td {
&:first-child {
border-left:none;
}
&:last-child {
border-right:none;
}
background:#fff;
padding:5px; padding:5px;
} }
.author, .author,
......
...@@ -13,7 +13,10 @@ ul { ...@@ -13,7 +13,10 @@ ul {
border-bottom: 1px solid rgba(0, 0, 0, 0.05); border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&.smoke { background-color:#f5f5f5; } &.smoke { background-color:#f5f5f5; }
&:hover { background:$hover; } &:hover {
background:$hover;
border-bottom:1px solid #ADF;
}
&:last-child { border:none } &:last-child { border:none }
.author { color: #999; } .author { color: #999; }
......
...@@ -11,6 +11,11 @@ table { ...@@ -11,6 +11,11 @@ table {
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
@include bg-dark-gray-gradient; @include bg-dark-gray-gradient;
ul.nav {
text-shadow: none;
margin: 0;
}
} }
th, td { th, td {
......
@import "bootstrap"; @import "bootstrap";
@import "bootstrap-responsive"; @import "bootstrap-responsive";
@import 'font-awesome';
/** GitLab colors **/ /** GitLab colors **/
$link_color:#3A89A3; $link_color:#3A89A3;
$blue_link: #2fa0bb; $blue_link: #2fa0bb;
$style_color: #474d57; $style_color: #474d57;
$hover: #fdf5d9; $hover: #D9EDF7;
/** GitLab Fonts **/ /** GitLab Fonts **/
@font-face { font-family: Korolev; src: url('korolev-medium-compressed.otf'); } @font-face { font-family: Korolev; src: font-url('korolev-medium-compressed.otf'); }
/** MIXINS **/ /** MIXINS **/
@mixin shade { @mixin shade {
...@@ -143,6 +144,7 @@ $hover: #fdf5d9; ...@@ -143,6 +144,7 @@ $hover: #fdf5d9;
@import "sections/projects.scss"; @import "sections/projects.scss";
@import "sections/merge_requests.scss"; @import "sections/merge_requests.scss";
@import "sections/graph.scss"; @import "sections/graph.scss";
@import "sections/events.scss";
/** /**
* This scss file redefine chozen selectbox styles for * This scss file redefine chozen selectbox styles for
...@@ -181,3 +183,9 @@ $hover: #fdf5d9; ...@@ -181,3 +183,9 @@ $hover: #fdf5d9;
* *
*/ */
@import "highlight/dark.scss"; @import "highlight/dark.scss";
/**
* File Editor styles
*
*/
@import "sections/editor.scss";
...@@ -47,6 +47,14 @@ ...@@ -47,6 +47,14 @@
padding-left: 32px; padding-left: 32px;
} }
.author,
.committer {
font-size:14px;
line-height:22px;
text-shadow:0 1px 1px #fff;
color:#777;
}
.avatar { .avatar {
margin-right: 10px; margin-right: 10px;
} }
...@@ -195,6 +203,11 @@ ...@@ -195,6 +203,11 @@
@extend .cgray; @extend .cgray;
} }
.notes_count {
float:right;
margin: -6px 8px 6px;
}
code { code {
background:#FCEEC1; background:#FCEEC1;
color:$style_color; color:$style_color;
...@@ -227,3 +240,13 @@ ...@@ -227,3 +240,13 @@
} }
} }
} }
.label_commit {
@include round-borders-all(4px);
padding:2px 4px;
border:none;
font-size:13px;
background: #474D57;
color:#fff;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
}
.file-editor {
#editor{
height: 500px;
width: 100%;
position: relative;
}
.editor-commit-comment {
padding-top:20px;
textarea {
width: 50%;
margin-left: 20px;
}
}
}
/**
* Events labels
*
*/
.event_label {
&.pushed {
padding:0 2px;
}
&.opened {
padding:0 2px;
}
&.closed {
padding:0 2px;
}
&.merged {
padding:0 2px;
}
&.left,
&.joined {
padding:0 2px;
float:none;
}
}
/**
* Dashboard events feed
*
*/
.event-item {
min-height:40px;
border-bottom:1px solid #eee;
.event-title {
color:#333;
font-weight: bold;
.author_name {
color:#333;
}
}
.event-body {
p {
color:#555;
}
.event-info {
color:#666;
}
}
.avatar {
width:32px;
}
.event_icon {
float: right;
border: 1px solid #EEE;
padding: 5px;
@include border-radius(5px);
background: #F9F9F9;
img {
width:20px;
}
}
ul {
margin-left:50px;
margin-bottom:5px;
.avatar {
width:18px;
margin-top:3px;
}
}
padding: 15px 5px;
&:last-child { border:none }
.wll:hover { background:none }
.event_commits {
margin-top: 5px;
li {
&.commit {
background: transparent;
padding:3px;
border:none;
font-size:12px;
}
&.commits-stat {
display: block;
margin-top: 5px;
}
}
}
}
/**
* Push event widget
*
*/
.event_lp {
@extend .ui-box;
color:#777;
margin-bottom:20px;
padding:8px;
@include border-radius(4px);
min-height:22px;
.avatar {
width:24px;
}
}
...@@ -65,6 +65,23 @@ input.check_all_issues { ...@@ -65,6 +65,23 @@ input.check_all_issues {
} }
} }
.btn.close_issue {
color: #B94A48;
font-weight: bold;
@include shade;
&:hover {
color: #B94A48;
}
}
.btn.reopen_issue {
color: #468847;
font-weight: bold;
@include shade;
&:hover {
color: #468847;
}
}
@media (min-width: 800px) { .issues_filters select { width:160px; } } @media (min-width: 800px) { .issues_filters select { width:160px; } }
@media (min-width: 1000px) { .issues_filters select { width:200px; } } @media (min-width: 1000px) { .issues_filters select { width:200px; } }
@media (min-width: 1200px) { .issues_filters select { width:220px; } } @media (min-width: 1200px) { .issues_filters select { width:220px; } }
...@@ -113,3 +130,17 @@ input.check_all_issues { ...@@ -113,3 +130,17 @@ input.check_all_issues {
.milestone { .milestone {
@extend .wll; @extend .wll;
} }
/**
* Fix milestone calendar
*/
.ui-datepicker {
border:none;
box-shadow:none;
.ui-datepicker-header {
@include solid_shade;
margin-bottom:10px;
border:1px solid #bbb;
}
}
...@@ -53,7 +53,7 @@ ul.main_menu { ...@@ -53,7 +53,7 @@ ul.main_menu {
border-left: 0; border-left: 0;
} }
&.current { &.active {
background-color:#D5D5D5; background-color:#D5D5D5;
border-right: 1px solid #BBB; border-right: 1px solid #BBB;
border-left: 1px solid #BBB; border-left: 1px solid #BBB;
......
...@@ -43,7 +43,9 @@ ...@@ -43,7 +43,9 @@
padding: 8px 0; padding: 8px 0;
overflow: hidden; overflow: hidden;
display: block; display: block;
position:relative;
img {float: left; margin-right: 10px;} img {float: left; margin-right: 10px;}
img.emoji {float:none;margin:0;}
.note-author cite{font-style: italic;} .note-author cite{font-style: italic;}
p { color:$style_color; } p { color:$style_color; }
.note-author { color: $style_color;} .note-author { color: $style_color;}
...@@ -55,7 +57,9 @@ ...@@ -55,7 +57,9 @@
.delete-note { .delete-note {
display:none; display:none;
float:right; position:absolute;
right:0;
top:0;
} }
&:hover { &:hover {
......
...@@ -7,18 +7,25 @@ ...@@ -7,18 +7,25 @@
@extend .span4; @extend .span4;
@extend .right; @extend .right;
.groups_box,
.projects_box { .projects_box {
h5 { h5 {
color:$style_color; color:$style_color;
font-size:16px; font-size:16px;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
padding: 2px 10px; padding: 2px 10px;
line-height:32px;
font-size:14px;
} }
ul { ul {
li { li {
padding:0; padding:0;
a { a {
display:block; display:block;
.group_name {
font-size:14px;
line-height:18px;
}
.project_name { .project_name {
color:#4fa2bd; color:#4fa2bd;
font-size:14px; font-size:14px;
...@@ -74,7 +81,7 @@ ...@@ -74,7 +81,7 @@
@include bg-gray-gradient; @include bg-gray-gradient;
padding: 4px 7px; padding: 4px 7px;
border: 1px solid #CCC; border: 1px solid #CCC;
margin-bottom:5px; margin-bottom:20px;
} }
.project_clone_holder { .project_clone_holder {
......
#tree-holder { .tree-holder {
#tree-content-holder { .tree-content-holder {
float:left; float:left;
width:100%; width:100%;
} }
#tree-readme-holder {
float:left;
width:100%;
.readme {
border:1px solid #ccc;
padding:12px;
background: #F7F7F7;
pre {
overflow: auto;
}
}
}
.tree_progress { .tree_progress {
display:none; display:none;
...@@ -25,14 +12,14 @@ ...@@ -25,14 +12,14 @@
} }
} }
#tree-slider { .tree-table {
@include border-radius(0); @include border-radius(0);
.tree-item { .tree-item {
&:hover { &:hover {
td { td {
background: $hover; background: $hover;
border-top:1px solid #FEA; border-top:1px solid #ADF;
border-bottom:1px solid #FEA; border-bottom:1px solid #ADF;
} }
cursor:pointer; cursor:pointer;
} }
...@@ -55,22 +42,44 @@ ...@@ -55,22 +42,44 @@
} }
} }
.tree-table {
#tree-slider { th .btn {
margin: -2px -1px;
padding: 2px 10px;
}
td { td {
background:#fafafa; background:#fafafa;
} }
} }
.tree-commit-link { .tree_author {
color:#333; padding-right: 8px;
img.avatar {
border: 0 none;
float: none;
margin-right: 0;
padding: 0;
width: 16px;
}
} }
a.tree-commit-link { .tree_commit {
color: #666; color: gray;
.tree-commit-link {
color: #444;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
} }
} }
}
}
.tree-btn-group {
.btn {
margin-right:-3px;
padding:2px 10px;
}
} }
class CommitLoad < BaseContext class CommitLoadContext < BaseContext
def execute def execute
result = { result = {
commit: nil, commit: nil,
......
class IssuesListContext < BaseContext
include IssuesHelper
attr_accessor :issues
def execute
@issues = case params[:f]
when issues_filter[:all] then @project.issues
when issues_filter[:closed] then @project.issues.closed
when issues_filter[:to_me] then @project.issues.opened.assigned(current_user)
else @project.issues.opened
end
@issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
@issues = @issues.includes(:author, :project).order("updated_at")
# Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present?
@issues = @issues.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?
@issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
end
@issues
end
end
class MergeRequestsLoad < BaseContext class MergeRequestsLoadContext < BaseContext
def execute def execute
type = params[:f] type = params[:f]
......
...@@ -13,7 +13,7 @@ module Notes ...@@ -13,7 +13,7 @@ module Notes
when "issue" when "issue"
project.issues.find(target_id).notes.inc_author.fresh.limit(20) project.issues.find(target_id).notes.inc_author.fresh.limit(20)
when "merge_request" when "merge_request"
project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20) project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh.limit(20)
when "snippet" when "snippet"
project.snippets.find(target_id).notes.fresh project.snippets.find(target_id).notes.fresh
when "wall" when "wall"
......
class SearchContext
attr_accessor :project_ids, :params
def initialize(project_ids, params)
@project_ids, @params = project_ids, params.dup
end
def execute
query = params[:search]
return result unless query.present?
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
end
def result
@result ||= {
projects: [],
merge_requests: [],
issues: []
}
end
end
class Admin::GroupsController < AdminController
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update]
def index
@groups = Group.scoped
@groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page]).per(20)
end
def show
@projects = Project.scoped
@projects = @projects.not_in_group(@group) if @group.projects.present?
@projects = @projects.all
end
def new
@group = Group.new
end
def edit
end
def create
@group = Group.new(params[:group])
@group.owner = current_user
if @group.save
redirect_to [:admin, @group], notice: 'Group was successfully created.'
else
render action: "new"
end
end
def update
group_params = params[:group].dup
owner_id =group_params.delete(:owner_id)
if owner_id
@group.owner = User.find(owner_id)
end
if @group.update_attributes(group_params)
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render action: "edit"
end
end
def project_update
project_ids = params[:project_ids]
Project.where(id: project_ids).update_all(group_id: @group.id)
redirect_to :back, notice: 'Group was successfully updated.'
end
def remove_project
@project = Project.find(params[:project_id])
@project.group_id = nil
@project.save
redirect_to :back, notice: 'Group was successfully updated.'
end
def destroy
@group.destroy
redirect_to groups_url, notice: 'Group was successfully deleted.'
end
private
def group
@group = Group.find_by_code(params[:id])
end
end
...@@ -30,7 +30,7 @@ class Admin::UsersController < AdminController ...@@ -30,7 +30,7 @@ class Admin::UsersController < AdminController
def new def new
@admin_user = User.new(projects_limit: Gitlab.config.default_projects_limit) @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin)
end end
def edit def edit
...@@ -60,7 +60,7 @@ class Admin::UsersController < AdminController ...@@ -60,7 +60,7 @@ class Admin::UsersController < AdminController
def create def create
admin = params[:user].delete("admin") admin = params[:user].delete("admin")
@admin_user = User.new(params[:user]) @admin_user = User.new(params[:user], as: :admin)
@admin_user.admin = (admin && admin.to_i > 0) @admin_user.admin = (admin && admin.to_i > 0)
respond_to do |format| respond_to do |format|
...@@ -86,7 +86,7 @@ class Admin::UsersController < AdminController ...@@ -86,7 +86,7 @@ class Admin::UsersController < AdminController
@admin_user.admin = (admin && admin.to_i > 0) @admin_user.admin = (admin && admin.to_i > 0)
respond_to do |format| respond_to do |format|
if @admin_user.update_attributes(params[:user]) if @admin_user.update_attributes(params[:user], as: :admin)
format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' } format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' }
format.json { head :ok } format.json { head :ok }
else else
......
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
before_filter :authenticate_user! before_filter :authenticate_user!
before_filter :reject_blocked! before_filter :reject_blocked!
before_filter :set_current_user_for_mailer
before_filter :check_token_auth
before_filter :set_current_user_for_observers before_filter :set_current_user_for_observers
before_filter :dev_tools if Rails.env == 'development' before_filter :dev_tools if Rails.env == 'development'
...@@ -11,28 +9,19 @@ class ApplicationController < ActionController::Base ...@@ -11,28 +9,19 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can? helper_method :abilities, :can?
rescue_from Gitlab::Gitolite::AccessDenied do |exception| rescue_from Gitlab::Gitolite::AccessDenied do |exception|
render "errors/gitolite", layout: "error", status: 500 render "errors/gitolite", layout: "errors", status: 500
end end
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
render "errors/encoding", layout: "error", status: 500 render "errors/encoding", layout: "errors", status: 500
end end
rescue_from ActiveRecord::RecordNotFound do |exception| rescue_from ActiveRecord::RecordNotFound do |exception|
render "errors/not_found", layout: "error", status: 404 render "errors/not_found", layout: "errors", status: 404
end end
layout :layout_by_resource
protected protected
def check_token_auth
# Redirect to login page if not atom feed
if params[:private_token].present? && params[:format] != 'atom'
redirect_to new_user_session_path
end
end
def reject_blocked! def reject_blocked!
if current_user && current_user.blocked if current_user && current_user.blocked
sign_out current_user sign_out current_user
...@@ -51,19 +40,8 @@ class ApplicationController < ActionController::Base ...@@ -51,19 +40,8 @@ class ApplicationController < ActionController::Base
end end
end end
def layout_by_resource
if devise_controller?
"devise_layout"
else
"application"
end
end
def set_current_user_for_mailer
MailerObserver.current_user = current_user
end
def set_current_user_for_observers def set_current_user_for_observers
MergeRequestObserver.current_user = current_user
IssueObserver.current_user = current_user IssueObserver.current_user = current_user
end end
...@@ -76,7 +54,7 @@ class ApplicationController < ActionController::Base ...@@ -76,7 +54,7 @@ class ApplicationController < ActionController::Base
end end
def project def project
@project ||= current_user.projects.find_by_code(params[:project_id]) @project ||= current_user.projects.find_by_code(params[:project_id] || params[:id])
@project || render_404 @project || render_404
end end
...@@ -93,15 +71,15 @@ class ApplicationController < ActionController::Base ...@@ -93,15 +71,15 @@ class ApplicationController < ActionController::Base
end end
def access_denied! def access_denied!
render "errors/access_denied", layout: "error", status: 404 render "errors/access_denied", layout: "errors", status: 404
end end
def not_found! def not_found!
render "errors/not_found", layout: "error", status: 404 render "errors/not_found", layout: "errors", status: 404
end end
def git_not_found! def git_not_found!
render "errors/git_not_found", layout: "error", status: 404 render "errors/git_not_found", layout: "errors", status: 404
end end
def method_missing(method_sym, *arguments, &block) def method_missing(method_sym, *arguments, &block)
...@@ -113,7 +91,7 @@ class ApplicationController < ActionController::Base ...@@ -113,7 +91,7 @@ class ApplicationController < ActionController::Base
end end
def render_404 def render_404
render file: File.join(Rails.root, "public", "404"), layout: false, status: "404" render file: Rails.root.join("public", "404"), layout: false, status: "404"
end end
def require_non_empty_project def require_non_empty_project
...@@ -126,10 +104,6 @@ class ApplicationController < ActionController::Base ...@@ -126,10 +104,6 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end end
def render_full_content
@full_content = true
end
def dev_tools def dev_tools
Rack::MiniProfiler.authorize_request Rack::MiniProfiler.authorize_request
end end
......
# Controller for viewing a file's blame
class BlameController < ProjectResourceController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :assign_ref_vars
def show
@repo = @project.repo
@blame = Grit::Blob.blame(@repo, @commit.id, @path)
end
end
# Controller for viewing a file's blame
class BlobController < ProjectResourceController
include ExtractsPath
include Gitlab::Encode
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :assign_ref_vars
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,
disposition: 'inline',
filename: @tree.name
)
else
not_found!
end
end
end
# Controller for a specific Commit
#
# Not to be confused with CommitsController, plural.
class CommitController < ProjectResourceController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
def show
result = CommitLoadContext.new(project, current_user, params).execute
@commit = result[:commit]
git_not_found! unless @commit
@suppress_diff = result[:suppress_diff]
@note = result[:note]
@line_notes = result[:line_notes]
@notes_count = result[:notes_count]
@comments_allowed = true
respond_to do |format|
format.html do
if result[:status] == :huge_commit
render "huge_commit" and return
end
end
format.patch
end
end
end
require "base64" require "base64"
class CommitsController < ApplicationController class CommitsController < ProjectResourceController
before_filter :project include ExtractsPath
layout "project"
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :load_refs, only: :index # load @branch, @tag & @ref
before_filter :render_full_content
def index def show
@repo = project.repo @repo = @project.repo
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0) @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @project.commits(@ref, params[:path], @limit, @offset) @commits = @project.commits(@ref, @path, @limit, @offset)
@commits = CommitDecorator.decorate(@commits) @commits = CommitDecorator.decorate(@commits)
respond_to do |format| respond_to do |format|
...@@ -25,54 +21,4 @@ class CommitsController < ApplicationController ...@@ -25,54 +21,4 @@ class CommitsController < ApplicationController
format.atom { render layout: false } format.atom { render layout: false }
end end
end end
def show
result = CommitLoad.new(project, current_user, params).execute
@commit = result[:commit]
if @commit
@suppress_diff = result[:suppress_diff]
@note = result[:note]
@line_notes = result[:line_notes]
@notes_count = result[:notes_count]
@comments_allowed = true
else
return git_not_found!
end
if result[:status] == :huge_commit
render "huge_commit" and return
end
end
def compare
result = Commit.compare(project, params[:from], params[:to])
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = []
@commits = CommitDecorator.decorate(@commits)
end
def patch
@commit = project.commit(params[:id])
send_data(
@commit.to_patch,
type: "text/plain",
disposition: 'attachment',
filename: "#{@commit.id}.patch"
)
end
protected
def load_refs
@ref ||= params[:ref].presence || params[:branch].presence || params[:tag].presence
@ref ||= @ref || @project.try(:default_branch) || 'master'
end
end end
class CompareController < ProjectResourceController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
def index
end
def show
result = Commit.compare(project, params[:from], params[:to])
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@refs_are_same = result[:same]
@line_notes = []
@commits = CommitDecorator.decorate(@commits)
end
def create
redirect_to project_compare_path(@project, params[:from], params[:to])
end
end
...@@ -2,8 +2,11 @@ class DashboardController < ApplicationController ...@@ -2,8 +2,11 @@ class DashboardController < ApplicationController
respond_to :html respond_to :html
def index def index
@projects = current_user.projects_with_events.page(params[:page]).per(40) @groups = Group.where(id: current_user.projects.pluck(:group_id))
@events = Event.recent_for_user(current_user).limit(20).offset(params[:offset] || 0) @projects = current_user.projects_with_events
@projects = @projects.page(params[:page]).per(30)
@events = Event.in_projects(current_user.project_ids).limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push @last_push = current_user.recent_push
respond_to do |format| respond_to do |format|
...@@ -16,14 +19,14 @@ class DashboardController < ApplicationController ...@@ -16,14 +19,14 @@ class DashboardController < ApplicationController
# Get authored or assigned open merge requests # Get authored or assigned open merge requests
def merge_requests def merge_requests
@projects = current_user.projects.all @projects = current_user.projects.all
@merge_requests = current_user.cared_merge_requests.order("created_at DESC").page(params[:page]).per(20) @merge_requests = current_user.cared_merge_requests.recent.page(params[:page]).per(20)
end end
# Get only assigned issues # Get only assigned issues
def issues def issues
@projects = current_user.projects.all @projects = current_user.projects.all
@user = current_user @user = current_user
@issues = current_user.assigned_issues.opened.order("created_at DESC").page(params[:page]).per(20) @issues = current_user.assigned_issues.opened.recent.page(params[:page]).per(20)
@issues = @issues.includes(:author, :project) @issues = @issues.includes(:author, :project)
respond_to do |format| respond_to do |format|
......
class DeployKeysController < ApplicationController class DeployKeysController < ProjectResourceController
respond_to :html respond_to :html
layout "project"
before_filter :project
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_admin_project! before_filter :authorize_admin_project!
def project
@project ||= Project.find_by_code(params[:project_id])
end
def index def index
@keys = @project.deploy_keys.all @keys = @project.deploy_keys.all
end end
......
class ErrorsController < ApplicationController class ErrorsController < ApplicationController
layout "error"
def githost def githost
render "errors/gitolite" render "errors/gitolite"
end end
......
class GroupsController < ApplicationController
respond_to :html
layout 'group'
before_filter :group
before_filter :projects
def show
@events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push
respond_to do |format|
format.html
format.js
format.atom { render layout: false }
end
end
# Get authored or assigned open merge requests
def merge_requests
@merge_requests = current_user.cared_merge_requests
@merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20)
end
# Get only assigned issues
def issues
@user = current_user
@issues = current_user.assigned_issues.opened
@issues = @issues.of_group(@group).recent.page(params[:page]).per(20)
@issues = @issues.includes(:author, :project)
respond_to do |format|
format.html
format.atom { render layout: false }
end
end
def search
result = SearchContext.new(project_ids, params).execute
@projects = result[:projects]
@merge_requests = result[:merge_requests]
@issues = result[:issues]
end
def people
@users = group.users.all
end
protected
def group
@group ||= Group.find_by_code(params[:id])
end
def projects
@projects ||= current_user.projects_with_events.where(group_id: @group.id)
end
def project_ids
projects.map(&:id)
end
end
class HooksController < ApplicationController class HooksController < ProjectResourceController
before_filter :authenticate_user!
before_filter :project
layout "project"
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_admin_project!, only: [:new, :create, :destroy] before_filter :authorize_admin_project!, only: [:new, :create, :destroy]
......
class IssuesController < ApplicationController class IssuesController < ProjectResourceController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled before_filter :module_enabled
before_filter :issue, only: [:edit, :update, :destroy, :show] before_filter :issue, only: [:edit, :update, :destroy, :show]
helper_method :issues_filter
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any issue # Allow read any issue
before_filter :authorize_read_issue! before_filter :authorize_read_issue!
...@@ -26,7 +18,6 @@ class IssuesController < ApplicationController ...@@ -26,7 +18,6 @@ class IssuesController < ApplicationController
def index def index
@issues = issues_filtered @issues = issues_filtered
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
respond_to do |format| respond_to do |format|
...@@ -141,35 +132,6 @@ class IssuesController < ApplicationController ...@@ -141,35 +132,6 @@ class IssuesController < ApplicationController
end end
def issues_filtered def issues_filtered
@issues = case params[:f] @issues = IssuesListContext.new(project, current_user, params).execute
when issues_filter[:all] then @project.issues
when issues_filter[:closed] then @project.issues.closed
when issues_filter[:to_me] then @project.issues.opened.assigned(current_user)
else @project.issues.opened
end
@issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
@issues = @issues.includes(:author, :project).order("updated_at")
# Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present?
@issues = @issues.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?
@issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
end
@issues
end
def issues_filter
{
all: "all",
closed: "closed",
to_me: "assigned-to-me",
open: "open"
}
end end
end end
class LabelsController < ApplicationController class LabelsController < ProjectResourceController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled before_filter :module_enabled
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any issue # Allow read any issue
before_filter :authorize_read_issue! before_filter :authorize_read_issue!
respond_to :js, :html respond_to :js, :html
def index def index
@labels = @project.issues.tag_counts_on(:labels).order('count DESC') @labels = @project.issues_labels.order('count DESC')
end end
protected protected
......
class MergeRequestsController < ApplicationController class MergeRequestsController < ProjectResourceController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled before_filter :module_enabled
before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw] before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check, :raw]
before_filter :validates_merge_request, only: [:show, :diffs, :raw] before_filter :validates_merge_request, only: [:show, :diffs, :raw]
before_filter :define_show_vars, only: [:show, :diffs] before_filter :define_show_vars, only: [:show, :diffs]
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any merge_request # Allow read any merge_request
before_filter :authorize_read_merge_request! before_filter :authorize_read_merge_request!
...@@ -24,7 +18,7 @@ class MergeRequestsController < ApplicationController ...@@ -24,7 +18,7 @@ class MergeRequestsController < ApplicationController
def index def index
@merge_requests = MergeRequestsLoad.new(project, current_user, params).execute @merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute
end end
def show def show
......
class MilestonesController < ApplicationController class MilestonesController < ProjectResourceController
before_filter :authenticate_user!
before_filter :project
before_filter :module_enabled before_filter :module_enabled
before_filter :milestone, only: [:edit, :update, :destroy, :show] before_filter :milestone, only: [:edit, :update, :destroy, :show]
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any milestone # Allow read any milestone
before_filter :authorize_read_milestone! before_filter :authorize_read_milestone!
...@@ -36,7 +30,7 @@ class MilestonesController < ApplicationController ...@@ -36,7 +30,7 @@ class MilestonesController < ApplicationController
end end
def show def show
@issues = @milestone.issues.opened.page(params[:page]).per(40) @issues = @milestone.issues
@users = @milestone.participants @users = @milestone.participants
respond_to do |format| respond_to do |format|
......
class NotesController < ApplicationController class NotesController < ProjectResourceController
before_filter :project
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_note! before_filter :authorize_read_note!
before_filter :authorize_write_note!, only: [:create] before_filter :authorize_write_note!, only: [:create]
...@@ -11,6 +7,11 @@ class NotesController < ApplicationController ...@@ -11,6 +7,11 @@ class NotesController < ApplicationController
def index def index
notes notes
if params[:target_type] == "merge_request"
@mixed_targets = true
@main_target_type = params[:target_type].camelize
end
respond_with(@notes) respond_with(@notes)
end end
......
class ProfileController < ApplicationController class ProfileController < ApplicationController
layout "profile"
before_filter :user before_filter :user
def show def show
......
class ProjectResourceController < ApplicationController
before_filter :project
# Authorize
before_filter :add_project_abilities
end
require Rails.root.join('lib', 'gitlab', 'graph_commit') require Rails.root.join('lib', 'gitlab', 'graph_commit')
class ProjectsController < ApplicationController class ProjectsController < ProjectResourceController
before_filter :project, except: [:index, :new, :create] skip_before_filter :project, only: [:new, :create]
layout :determine_layout
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!, except: [:index, :new, :create] before_filter :authorize_read_project!, except: [:index, :new, :create]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy] before_filter :authorize_admin_project!, only: [:edit, :update, :destroy]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph] before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'application', only: [:new, :create]
def new def new
@project = Project.new @project = Project.new
end end
...@@ -46,7 +46,7 @@ class ProjectsController < ApplicationController ...@@ -46,7 +46,7 @@ class ProjectsController < ApplicationController
def show def show
limit = (params[:limit] || 20).to_i limit = (params[:limit] || 20).to_i
@events = @project.events.recent.limit(limit) @events = @project.events.recent.limit(limit).offset(params[:offset] || 0)
respond_to do |format| respond_to do |format|
format.html do format.html do
...@@ -57,6 +57,7 @@ class ProjectsController < ApplicationController ...@@ -57,6 +57,7 @@ class ProjectsController < ApplicationController
render "projects/empty" render "projects/empty"
end end
end end
format.js
end end
end end
...@@ -92,19 +93,4 @@ class ProjectsController < ApplicationController ...@@ -92,19 +93,4 @@ class ProjectsController < ApplicationController
format.html { redirect_to root_path } format.html { redirect_to root_path }
end end
end end
protected
def project
@project ||= Project.find_by_code(params[:id])
@project || render_404
end
def determine_layout
if @project && !@project.new_record?
"project"
else
"application"
end
end
end end
class ProtectedBranchesController < ApplicationController class ProtectedBranchesController < ProjectResourceController
before_filter :project
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_admin_project!, only: [:destroy, :create] before_filter :authorize_admin_project!, only: [:destroy, :create]
before_filter :render_full_content
layout "project"
def index def index
@branches = @project.protected_branches.all @branches = @project.protected_branches.all
......
require 'github/markup' class RefsController < ProjectResourceController
class RefsController < ApplicationController
include Gitlab::Encode include Gitlab::Encode
before_filter :project
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :ref before_filter :ref
before_filter :define_tree_vars, only: [:tree, :blob, :blame, :logs_tree] before_filter :define_tree_vars, only: [:blob, :logs_tree]
before_filter :render_full_content
layout "project"
def switch def switch
respond_to do |format| respond_to do |format|
format.html do format.html do
new_path = if params[:destination] == "tree" new_path = if params[:destination] == "tree"
tree_project_ref_path(@project, params[:ref]) project_tree_path(@project, @ref)
else else
project_commits_path(@project, ref: params[:ref]) project_commits_path(@project, @ref)
end end
redirect_to new_path redirect_to new_path
...@@ -35,19 +28,6 @@ class RefsController < ApplicationController ...@@ -35,19 +28,6 @@ class RefsController < ApplicationController
end end
end end
#
# Repository preview
#
def tree
respond_to do |format|
format.html
format.js do
# disable cache to allow back button works
no_cache_headers
end
end
end
def logs_tree def logs_tree
contents = @tree.contents contents = @tree.contents
@logs = contents.map do |content| @logs = contents.map do |content|
...@@ -61,30 +41,6 @@ class RefsController < ApplicationController ...@@ -61,30 +41,6 @@ class RefsController < ApplicationController
end end
end end
def blob
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,
disposition: 'inline',
filename: @tree.name
)
else
head(404)
end
end
def blame
@blame = Grit::Blob.blame(@repo, @commit.id, params[:path])
end
protected protected
def define_tree_vars def define_tree_vars
...@@ -95,13 +51,11 @@ class RefsController < ApplicationController ...@@ -95,13 +51,11 @@ class RefsController < ApplicationController
@commit = CommitDecorator.decorate(@commit) @commit = CommitDecorator.decorate(@commit)
@tree = Tree.new(@commit.tree, project, @ref, params[:path]) @tree = Tree.new(@commit.tree, project, @ref, params[:path])
@tree = TreeDecorator.new(@tree) @tree = TreeDecorator.new(@tree)
@hex_path = Digest::SHA1.hexdigest(params[:path] || "/") @hex_path = Digest::SHA1.hexdigest(params[:path] || "")
if params[:path] if params[:path]
@history_path = tree_file_project_ref_path(@project, @ref, params[:path])
@logs_path = logs_file_project_ref_path(@project, @ref, params[:path]) @logs_path = logs_file_project_ref_path(@project, @ref, params[:path])
else else
@history_path = tree_project_ref_path(@project, @ref)
@logs_path = logs_tree_project_ref_path(@project, @ref) @logs_path = logs_tree_project_ref_path(@project, @ref)
end end
rescue rescue
...@@ -109,6 +63,6 @@ class RefsController < ApplicationController ...@@ -109,6 +63,6 @@ class RefsController < ApplicationController
end end
def ref def ref
@ref = params[:id] @ref = params[:id] || params[:ref]
end end
end end
class RepositoriesController < ApplicationController class RepositoriesController < ProjectResourceController
before_filter :project
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_code_access! before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :render_full_content
layout "project"
def show def show
@activities = @project.commits_with_refs(20) @activities = @project.commits_with_refs(20)
end end
def branches def branches
@branches = @project.repo.heads.sort_by(&:name) @branches = @project.branches
end end
def tags def tags
@tags = @project.repo.tags.sort_by(&:name).reverse @tags = @project.tags
end end
def archive def archive
......
class SearchController < ApplicationController class SearchController < ApplicationController
def show def show
query = params[:search] result = SearchContext.new(current_user.project_ids, params).execute
@projects = [] @projects = result[:projects]
@merge_requests = [] @merge_requests = result[:merge_requests]
@issues = [] @issues = result[:issues]
if query.present?
@projects = current_user.projects.search(query).limit(10)
@merge_requests = MergeRequest.where(project_id: current_user.project_ids).search(query).limit(10)
@issues = Issue.where(project_id: current_user.project_ids).search(query).limit(10)
end
end end
end end
class SnippetsController < ApplicationController class SnippetsController < ProjectResourceController
before_filter :authenticate_user!
before_filter :project
before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw] before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any snippet # Allow read any snippet
before_filter :authorize_read_snippet! before_filter :authorize_read_snippet!
...@@ -56,7 +50,6 @@ class SnippetsController < ApplicationController ...@@ -56,7 +50,6 @@ class SnippetsController < ApplicationController
def show def show
@note = @project.notes.new(noteable: @snippet) @note = @project.notes.new(noteable: @snippet)
render_full_content
end end
def destroy def destroy
......
class TeamMembersController < ApplicationController class TeamMembersController < ProjectResourceController
before_filter :project
layout "project"
# Authorize # Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project! before_filter :authorize_read_project!
before_filter :authorize_admin_project!, except: [:index, :show] before_filter :authorize_admin_project!, except: [:index, :show]
......
# Controller for viewing a repository's file structure
class TreeController < ProjectResourceController
include ExtractsPath
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :edit_requirements, only: [:edit, :update]
def show
@hex_path = Digest::SHA1.hexdigest(@path)
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
respond_to do |format|
format.html
# Disable cache so browser history works
format.js { no_cache_headers }
end
end
def edit
@last_commit = @project.last_commit_for(@ref, @path).sha
end
def update
file_editor = Gitlab::FileEditor.new(current_user, @project, @ref)
update_status = file_editor.update(
@path,
params[:content],
params[:commit_message],
params[:last_commit]
)
if update_status
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"
render :edit
end
end
private
def edit_requirements
unless @tree.is_blob? && @tree.text?
redirect_to project_tree_path(@project, @id), notice: "You can only edit text files"
end
allowed = if project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, project)
else
can?(current_user, :push_code, project)
end
return access_denied! unless allowed
end
end
class WikisController < ApplicationController class WikisController < ProjectResourceController
before_filter :project
before_filter :add_project_abilities
before_filter :authorize_read_wiki! before_filter :authorize_read_wiki!
before_filter :authorize_write_wiki!, only: [:edit, :create, :history] before_filter :authorize_write_wiki!, only: [:edit, :create, :history]
before_filter :authorize_admin_wiki!, only: :destroy before_filter :authorize_admin_wiki!, only: :destroy
layout "project"
def pages def pages
@wikis = @project.wikis.group(:slug).order("created_at") @wikis = @project.wikis.group(:slug).order("created_at")
......
...@@ -16,13 +16,15 @@ class CommitDecorator < ApplicationDecorator ...@@ -16,13 +16,15 @@ class CommitDecorator < ApplicationDecorator
# In case this first line is longer than 80 characters, it is cut off # In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended. # after 70 characters and ellipses (`&hellp;`) are appended.
def title def title
return no_commit_message if safe_message.blank? title = safe_message
title_end = safe_message.index(/\n/) return no_commit_message if title.blank?
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
safe_message[0..69] << "&hellip;".html_safe title_end = title.index(/\n/)
if (!title_end && title.length > 80) || (title_end && title_end > 80)
title[0..69] << "&hellip;".html_safe
else else
safe_message.split(/\n/, 2).first title.split(/\n/, 2).first
end end
end end
...@@ -30,11 +32,35 @@ class CommitDecorator < ApplicationDecorator ...@@ -30,11 +32,35 @@ class CommitDecorator < ApplicationDecorator
# #
# cut off, ellipses (`&hellp;`) are prepended to the commit message. # cut off, ellipses (`&hellp;`) are prepended to the commit message.
def description def description
title_end = safe_message.index(/\n/) description = safe_message
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << safe_message[70..-1] title_end = description.index(/\n/)
if (!title_end && description.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << description[70..-1]
else
description.split(/\n/, 2)[1].try(:chomp)
end
end
# Returns a link to the commit author. If the author 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 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
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 else
safe_message.split(/\n/, 2)[1].try(:chomp) h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-author-link"
end end
end end
......
...@@ -3,11 +3,11 @@ class EventDecorator < ApplicationDecorator ...@@ -3,11 +3,11 @@ class EventDecorator < ApplicationDecorator
def feed_title def feed_title
if self.issue? if self.issue?
"#{self.author_name} #{self.action_name} issue ##{self.target_id}:" + self.issue_title "#{self.author_name} #{self.action_name} issue ##{self.target_id}: #{self.issue_title} at #{self.project.name}"
elsif self.merge_request? elsif self.merge_request?
"#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title "#{self.author_name} #{self.action_name} MR ##{self.target_id}: #{self.merge_request_title} at #{self.project.name}"
elsif self.push? elsif self.push?
"#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name "#{self.author_name} #{self.push_action_name} #{self.ref_type} #{self.ref_name} at #{self.project.name}"
elsif self.membership_changed? elsif self.membership_changed?
"#{self.author_name} #{self.action_name} #{self.project.name}" "#{self.author_name} #{self.action_name} #{self.project.name}"
else else
...@@ -20,8 +20,25 @@ class EventDecorator < ApplicationDecorator ...@@ -20,8 +20,25 @@ class EventDecorator < ApplicationDecorator
h.project_issue_url(self.project, self.issue) h.project_issue_url(self.project, self.issue)
elsif self.merge_request? elsif self.merge_request?
h.project_merge_request_url(self.project, self.merge_request) h.project_merge_request_url(self.project, self.merge_request)
elsif self.push?
if self.push_with_commits?
if self.commits_count > 1
h.project_compare_url(self.project, :from => self.parent_commit.id, :to => self.last_commit.id)
else
h.project_commit_url(self.project, :id => self.last_commit.id)
end
else
h.project_commits_url(self.project, self.ref_name)
end
end
end
def feed_summary
if self.issue?
h.render "events/event_issue", issue: self.issue
elsif self.push? elsif self.push?
h.project_commits_url(self.project, ref: self.ref_name) h.render "events/event_push", event: self
end end
end end
end end
...@@ -8,37 +8,28 @@ class TreeDecorator < ApplicationDecorator ...@@ -8,37 +8,28 @@ class TreeDecorator < ApplicationDecorator
#parts = parts[0...-1] if is_blob? #parts = parts[0...-1] if is_blob?
yield(h.link_to("..", "#", remote: :true)) if parts.count > max_links yield(h.link_to("..", "#", remote: true)) if parts.count > max_links
parts.each do |part| parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty? part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty? part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links next unless parts.last(2).include?(part) if parts.count > max_links
yield(h.link_to(h.truncate(part, length: 40), h.tree_file_project_ref_path(project, ref, path: part_path), remote: :true)) yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)), remote: true))
end end
end end
end end
def up_dir? def up_dir?
!!path path.present?
end end
def up_dir_path def up_dir_path
file = File.join(path, "..") file = File.join(path, "..")
h.tree_file_project_ref_path(project, ref, file) h.project_tree_path(project, h.tree_join(ref, file))
end end
def history_path def readme
h.project_commits_path(project, path: path, ref: ref) @readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
end
def mb_size
size = (tree.size / 1024)
if size < 1024
"#{size} KB"
else
"#{size/1024} MB"
end
end end
end end
require 'digest/md5' require 'digest/md5'
module ApplicationHelper module ApplicationHelper
# Check if a particular controller is the current one
#
# args - One or more controller names to check
#
# Examples
#
# # On TreeController
# current_controller?(:tree) # => true
# current_controller?(:commits) # => false
# current_controller?(:commits, :tree) # => true
def current_controller?(*args)
args.any? { |v| v.to_s.downcase == controller.controller_name }
end
# Check if a partcular action is the current one
#
# args - One or more action names to check
#
# Examples
#
# # On Projects#new
# current_action?(:new) # => true
# current_action?(:create) # => false
# current_action?(:new, :create) # => true
def current_action?(*args)
args.any? { |v| v.to_s.downcase == action_name }
end
def gravatar_icon(user_email = '', size = 40) def gravatar_icon(user_email = '', size = 40)
if Gitlab.config.disable_gravatar? || user_email.blank? if Gitlab.config.disable_gravatar? || user_email.blank?
'no_avatar.png' 'no_avatar.png'
...@@ -31,8 +60,8 @@ module ApplicationHelper ...@@ -31,8 +60,8 @@ module ApplicationHelper
def grouped_options_refs(destination = :tree) def grouped_options_refs(destination = :tree)
options = [ options = [
["Branch", @project.repo.heads.map(&:name) ], ["Branch", @project.branch_names ],
[ "Tag", @project.tags ] [ "Tag", @project.tag_names ]
] ]
# If reference is commit id - # If reference is commit id -
...@@ -47,26 +76,46 @@ module ApplicationHelper ...@@ -47,26 +76,46 @@ module ApplicationHelper
def search_autocomplete_source def search_autocomplete_source
projects = current_user.projects.map{ |p| { label: p.name, url: project_path(p) } } projects = current_user.projects.map{ |p| { label: p.name, url: project_path(p) } }
default_nav = [ default_nav = [
{ label: "Profile", url: profile_path }, { label: "My Profile", url: profile_path },
{ label: "Keys", url: keys_path }, { label: "My SSH Keys", url: keys_path },
{ label: "Dashboard", url: root_path }, { label: "My Dashboard", url: root_path },
{ label: "Admin", url: admin_root_path } { label: "Admin Section", url: admin_root_path },
] ]
project_nav = [] help_nav = [
{ label: "Workflow Help", url: help_workflow_path },
{ label: "Permissions Help", url: help_permissions_path },
{ label: "Web Hooks Help", url: help_web_hooks_path },
{ label: "System Hooks Help", url: help_system_hooks_path },
{ label: "API Help", url: help_api_path },
{ label: "Markdown Help", url: help_markdown_path },
{ label: "SSH Keys Help", url: help_ssh_path },
]
project_nav = []
if @project && !@project.new_record? if @project && !@project.new_record?
project_nav = [ project_nav = [
{ label: "#{@project.name} / Issues", url: project_issues_path(@project) }, { label: "#{@project.name} Issues", url: project_issues_path(@project) },
{ label: "#{@project.name} / Wall", url: wall_project_path(@project) }, { label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.root_ref) },
{ label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) }, { label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) },
{ label: "#{@project.name} / Commits", url: project_commits_path(@project) }, { label: "#{@project.name} Milestones", url: project_milestones_path(@project) },
{ label: "#{@project.name} / Team", url: project_team_index_path(@project) } { label: "#{@project.name} Snippets", url: project_snippets_path(@project) },
{ label: "#{@project.name} Team", url: project_team_index_path(@project) },
{ label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.root_ref) },
{ label: "#{@project.name} Wall", url: wall_project_path(@project) },
{ label: "#{@project.name} Wiki", url: project_wikis_path(@project) },
] ]
end end
[projects, default_nav, project_nav].flatten.to_json [projects, default_nav, project_nav, help_nav].flatten.to_json
end
def emoji_autocomplete_source
# should be an array of strings
# so to_s can be called, because it is sufficient and to_json is too slow
Emoji.names.to_s
end end
def ldap_enable? def ldap_enable?
...@@ -85,45 +134,6 @@ module ApplicationHelper ...@@ -85,45 +134,6 @@ module ApplicationHelper
event.project.merge_requests_enabled event.project.merge_requests_enabled
end end
def tab_class(tab_key)
active = case tab_key
# Project Area
when :wall; wall_tab?
when :wiki; controller.controller_name == "wikis"
when :issues; issues_tab?
when :network; current_page?(controller: "projects", action: "graph", id: @project)
when :merge_requests; controller.controller_name == "merge_requests"
# Dashboard Area
when :help; controller.controller_name == "help"
when :search; current_page?(search_path)
when :dash_issues; current_page?(dashboard_issues_path)
when :dash_mr; current_page?(dashboard_merge_requests_path)
when :root; current_page?(dashboard_path) || current_page?(root_path)
# Profile Area
when :profile; current_page?(controller: "profile", action: :show)
when :history; current_page?(controller: "profile", action: :history)
when :account; current_page?(controller: "profile", action: :account)
when :token; current_page?(controller: "profile", action: :token)
when :design; current_page?(controller: "profile", action: :design)
when :ssh_keys; controller.controller_name == "keys"
# Admin Area
when :admin_root; controller.controller_name == "dashboard"
when :admin_users; controller.controller_name == 'users'
when :admin_projects; controller.controller_name == "projects"
when :admin_hooks; controller.controller_name == 'hooks'
when :admin_resque; controller.controller_name == 'resque'
when :admin_logs; controller.controller_name == 'logs'
else
false
end
active ? "current" : nil
end
def hexdigest(string) def hexdigest(string)
Digest::SHA1.hexdigest string Digest::SHA1.hexdigest string
end end
......
module EventsHelper
def link_to_author(event)
project = event.project
tm = project.team_member_by_id(event.author_id) if project
if tm
link_to event.author_name, project_team_member_path(project, tm)
else
event.author_name
end
end
def event_action_name(event)
target = if event.target_type
event.target_type.titleize.downcase
else
'project'
end
[event.action_name, target].join(" ")
end
def event_image event
event_image_path = if event.push?
"event_push.png"
elsif event.merged?
"event_mr_merged.png"
end
return nil unless event_image_path
content_tag :div, class: 'event_icon' do
image_tag event_image_path
end
end
end
...@@ -43,4 +43,13 @@ module IssuesHelper ...@@ -43,4 +43,13 @@ module IssuesHelper
# Milestone uses :title, Issue uses :name # Milestone uses :title, Issue uses :name
OpenStruct.new(id: 0, title: 'Unspecified', name: 'Unassigned') OpenStruct.new(id: 0, title: 'Unspecified', name: 'Unassigned')
end end
def issues_filter
{
all: "all",
closed: "closed",
to_me: "assigned-to-me",
open: "open"
}
end
end end
...@@ -7,11 +7,18 @@ module NotesHelper ...@@ -7,11 +7,18 @@ module NotesHelper
params[:loading_new].present? params[:loading_new].present?
end end
def note_vote_class(note) # Helps to distinguish e.g. commit notes in mr notes list
if note.upvote? def note_for_main_target?(note)
"vote upvote" !@mixed_targets || @main_target_type == note.noteable_type
elsif note.downvote?
"vote downvote"
end end
def link_to_commit_diff_line_note(note)
commit = note.noteable
diff_index, diff_old_line, diff_new_line = note.line_code.split('_')
link_file = commit.diffs[diff_index.to_i].new_path
link_line = diff_new_line
link_to "#{link_file}:L#{link_line}", project_commit_path(@project, commit, anchor: note.line_code)
end end
end end
...@@ -6,5 +6,9 @@ module ProjectsHelper ...@@ -6,5 +6,9 @@ module ProjectsHelper
def remove_from_team_message(project, member) def remove_from_team_message(project, member)
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?" "You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
end end
def link_to_project project
link_to project.name, project
end
end end
module TabHelper module TabHelper
def issues_tab? # Navigation link helper
controller.controller_name == "issues" || controller.controller_name == "milestones" #
# Returns an `li` element with an 'active' class if the supplied
# controller(s) and/or action(s) are currently active. The content of the
# element is the value passed to the block.
#
# options - The options hash used to determine if the element is "active" (default: {})
# :controller - One or more controller names to check (optional).
# :action - One or more action names to check (optional).
# :path - A shorthand path, such as 'dashboard#index', to check (optional).
# :html_options - Extra options to be passed to the list element (optional).
# block - An optional block that will become the contents of the returned
# `li` element.
#
# When both :controller and :action are specified, BOTH must match in order
# to be marked as active. When only one is given, either can match.
#
# Examples
#
# # Assuming we're on TreeController#show
#
# # Controller matches, but action doesn't
# nav_link(controller: [:tree, :refs], action: :edit) { "Hello" }
# # => '<li>Hello</li>'
#
# # Controller matches
# nav_link(controller: [:tree, :refs]) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Shorthand path
# nav_link(path: 'tree#show') { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Supplying custom options for the list element
# nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
# # => '<li class="home active">Hello</li>'
#
# Returns a list item element String
def nav_link(options = {}, &block)
if path = options.delete(:path)
c, a, _ = path.split('#')
else
c = options.delete(:controller)
a = options.delete(:action)
end end
def wall_tab? if c && a
current_page?(controller: "projects", action: "wall", id: @project) # When given both options, make sure BOTH are active
klass = current_controller?(*c) && current_action?(*a) ? 'active' : ''
else
# Otherwise check EITHER option
klass = current_controller?(*c) || current_action?(*a) ? 'active' : ''
end end
def project_tab_class # Add our custom class into the html_options, which may or may not exist
[:show, :files, :edit, :update].each do |action| # and which may or may not already have a :class key
return "current" if current_page?(controller: "projects", action: action, id: @project) o = options.delete(:html_options) || {}
end o[:class] ||= ''
o[:class] += ' ' + klass
o[:class].strip!
if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name if block_given?
"current" content_tag(:li, capture(&block), o)
else
content_tag(:li, nil, o)
end end
end end
def tree_tab_class def project_tab_class
controller.controller_name == "refs" ? "current" : nil [:show, :files, :edit, :update].each do |action|
return "active" if current_page?(controller: "projects", action: action, id: @project)
end end
def commit_tab_class if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
if ['commits', 'repositories', 'protected_branches'].include? controller.controller_name "active"
"current"
end end
end end
def branches_tab_class def branches_tab_class
if current_page?(branches_project_repository_path(@project)) || if current_page?(branches_project_repository_path(@project)) ||
controller.controller_name == "protected_branches" || current_controller?(:protected_branches) ||
current_page?(project_repository_path(@project)) current_page?(project_repository_path(@project))
'active' 'active'
end end
......
module TreeHelper module TreeHelper
def tree_icon(content) # Sorts a repository's tree so that folders are before files and renders
if content.is_a?(Grit::Blob) # their corresponding partials
if content.text? #
image_tag "file_txt.png" # contents - A Grit::Tree object for the current tree
elsif content.image? def render_tree(contents)
image_tag "file_img.png" # Render Folders before Files/Submodules
else folders, files = contents.partition { |v| v.kind_of?(Grit::Tree) }
image_tag "file_bin.png"
end tree = ""
# Render folders if we have any
tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
files.each do |f|
if f.respond_to?(:url)
# Object is a Submodule
tree += render partial: 'tree/submodule_item', object: f
else else
image_tag "file_dir.png" # Object is a Blob
tree += render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
end end
end end
def tree_hex_class(content) tree.html_safe
"file_#{hexdigest(content.name)}"
end end
def tree_full_path(content) # Return an image icon depending on the file type
content.name.force_encoding('utf-8') #
if params[:path] # type - String type of the tree item; either 'folder' or 'file'
File.join(params[:path], content.name) def tree_icon(type)
else image = type == 'folder' ? 'file_dir.png' : 'file_txt.png'
content.name image_tag(image, size: '16x16')
end end
def tree_hex_class(content)
"file_#{hexdigest(content.name)}"
end end
# Public: Determines if a given filename is compatible with GitHub::Markup. # Public: Determines if a given filename is compatible with GitHub::Markup.
...@@ -39,4 +50,21 @@ module TreeHelper ...@@ -39,4 +50,21 @@ module TreeHelper
def gitlab_markdown?(filename) def gitlab_markdown?(filename)
filename.end_with?(*%w(.mdown .md .markdown)) filename.end_with?(*%w(.mdown .md .markdown))
end end
def plain_text_readme? filename
filename == 'README'
end
# Simple shortcut to File.join
def tree_join(*args)
File.join(*args)
end
def allowed_tree_edit?
if @project.protected_branch? @ref
can?(current_user, :push_code_to_protected_branches, @project)
else
can?(current_user, :push_code, @project)
end
end
end end
...@@ -9,11 +9,11 @@ class Notify < ActionMailer::Base ...@@ -9,11 +9,11 @@ class Notify < ActionMailer::Base
default from: Gitlab.config.email_from default from: Gitlab.config.email_from
def new_user_email(user_id, password)
@user = User.find(user_id)
@password = password #
mail(to: @user.email, subject: subject("Account was created for you")) # Issue
end #
def new_issue_email(issue_id) def new_issue_email(issue_id)
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
...@@ -21,20 +21,61 @@ class Notify < ActionMailer::Base ...@@ -21,20 +21,61 @@ class Notify < ActionMailer::Base
mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title)) mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title))
end end
def note_wall_email(recipient_id, note_id) def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@note = Note.find(note_id) @issue = Issue.find(issue_id)
@project = @note.project @previous_assignee ||= User.find(previous_assignee_id)
mail(to: recipient(recipient_id), subject: subject) @project = @issue.project
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.id}", @issue.title))
end
#
# Merge Request
#
def new_merge_request_email(merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee ||= User.find(previous_assignee_id)
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
end end
#
# Note
#
def note_commit_email(recipient_id, note_id) def note_commit_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@commit = @note.target @commit = @note.noteable
@commit = CommitDecorator.decorate(@commit) @commit = CommitDecorator.decorate(@commit)
@project = @note.project @project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
end end
def note_issue_email(recipient_id, note_id)
@note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}"))
end
def note_merge_request_email(recipient_id, note_id) def note_merge_request_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@merge_request = @note.noteable @merge_request = @note.noteable
...@@ -42,11 +83,10 @@ class Notify < ActionMailer::Base ...@@ -42,11 +83,10 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}")) mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}"))
end end
def note_issue_email(recipient_id, note_id) def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project @project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}")) mail(to: recipient(recipient_id), subject: subject)
end end
def note_wiki_email(recipient_id, note_id) def note_wiki_email(recipient_id, note_id)
...@@ -56,25 +96,11 @@ class Notify < ActionMailer::Base ...@@ -56,25 +96,11 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject("note for wiki")) mail(to: recipient(recipient_id), subject: subject("note for wiki"))
end end
def new_merge_request_email(merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee ||= User.find(previous_assignee_id)
@project = @merge_request.project
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) #
@issue = Issue.find(issue_id) # Project
@previous_assignee ||= User.find(previous_assignee_id) #
@project = @issue.project
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
end
def project_access_granted_email(user_project_id) def project_access_granted_email(user_project_id)
@users_project = UsersProject.find user_project_id @users_project = UsersProject.find user_project_id
...@@ -83,14 +109,19 @@ class Notify < ActionMailer::Base ...@@ -83,14 +109,19 @@ class Notify < ActionMailer::Base
subject: subject("access to project was granted")) subject: subject("access to project was granted"))
end end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status #
@updated_by = User.find updated_by_user_id # User
mail(to: recipient(recipient_id), #
subject: subject("changed issue ##{@issue.id}", @issue.title))
def new_user_email(user_id, password)
@user = User.find(user_id)
@password = password
mail(to: @user.email, subject: subject("Account was created for you"))
end end
private private
# Look up a User by their ID and return their email address # Look up a User by their ID and return their email address
......
class Ability class Ability
def self.allowed(object, subject) class << self
def allowed(object, subject)
case subject.class.name case subject.class.name
when "Project" then project_abilities(object, subject) when "Project" then project_abilities(object, subject)
when "Issue" then issue_abilities(object, subject) when "Issue" then issue_abilities(object, subject)
...@@ -10,7 +11,7 @@ class Ability ...@@ -10,7 +11,7 @@ class Ability
end end
end end
def self.project_abilities(user, project) def project_abilities(user, project)
rules = [] rules = []
rules << [ rules << [
...@@ -34,9 +35,14 @@ class Ability ...@@ -34,9 +35,14 @@ class Ability
] if project.report_access_for?(user) ] if project.report_access_for?(user)
rules << [ rules << [
:write_wiki :write_wiki,
:push_code
] if project.dev_access_for?(user) ] if project.dev_access_for?(user)
rules << [
:push_code_to_protected_branches
] if project.master_access_for?(user)
rules << [ rules << [
:modify_issue, :modify_issue,
:modify_snippet, :modify_snippet,
...@@ -52,11 +58,9 @@ class Ability ...@@ -52,11 +58,9 @@ class Ability
:admin_wiki :admin_wiki
] if project.master_access_for?(user) || project.owner == user ] if project.master_access_for?(user) || project.owner == user
rules.flatten rules.flatten
end end
class << self
[:issue, :note, :snippet, :merge_request].each do |name| [:issue, :note, :snippet, :merge_request].each do |name|
define_method "#{name}_abilities" do |user, subject| define_method "#{name}_abilities" do |user, subject|
if subject.author == user if subject.author == user
...@@ -73,8 +77,7 @@ class Ability ...@@ -73,8 +77,7 @@ class Ability
:"modify_#{name}", :"modify_#{name}",
] ]
else else
subject.respond_to?(:project) ? subject.respond_to?(:project) ? project_abilities(user, subject.project) : []
project_abilities(user, subject.project) : []
end end
end end
end end
......
...@@ -4,24 +4,11 @@ class Commit ...@@ -4,24 +4,11 @@ class Commit
include StaticModel include StaticModel
extend ActiveModel::Naming extend ActiveModel::Naming
attr_accessor :commit attr_accessor :commit, :head, :refs
attr_accessor :head
attr_accessor :refs delegate :message, :authored_date, :committed_date, :parents, :sha,
:date, :committer, :author, :message, :diffs, :tree, :id,
delegate :message, :to_patch, to: :commit
:authored_date,
:committed_date,
:parents,
:sha,
:date,
:committer,
:author,
:message,
:diffs,
:tree,
:id,
:to_patch,
to: :commit
class << self class << self
def find_or_first(repo, commit_id = nil, root_ref) def find_or_first(repo, commit_id = nil, root_ref)
...@@ -30,6 +17,7 @@ class Commit ...@@ -30,6 +17,7 @@ class Commit
else else
repo.commits(root_ref).first repo.commits(root_ref).first
end end
Commit.new(commit) if commit Commit.new(commit) if commit
end end
...@@ -119,7 +107,7 @@ class Commit ...@@ -119,7 +107,7 @@ class Commit
end end
def safe_message def safe_message
utf8 message @safe_message ||= utf8 message
end end
def created_at def created_at
......
class Event < ActiveRecord::Base class Event < ActiveRecord::Base
include PushEvent include PushEvent
attr_accessible :project, :action, :data, :author_id, :project_id,
:target_id, :target_type
default_scope where("author_id IS NOT NULL") default_scope where("author_id IS NOT NULL")
Created = 1 Created = 1
...@@ -13,25 +16,30 @@ class Event < ActiveRecord::Base ...@@ -13,25 +16,30 @@ class Event < ActiveRecord::Base
Joined = 8 # User joined project Joined = 8 # User joined project
Left = 9 # User left project Left = 9 # User left project
delegate :name, :email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true
belongs_to :author, class_name: "User"
belongs_to :project belongs_to :project
belongs_to :target, polymorphic: true belongs_to :target, polymorphic: true
# For Hash only # For Hash only
serialize :data serialize :data
# Scopes
scope :recent, order("created_at DESC") scope :recent, order("created_at DESC")
scope :code_push, where(action: Pushed) scope :code_push, where(action: Pushed)
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
def self.determine_action(record) class << self
def determine_action(record)
if [Issue, MergeRequest].include? record.class if [Issue, MergeRequest].include? record.class
Event::Created Event::Created
elsif record.kind_of? Note elsif record.kind_of? Note
Event::Commented Event::Commented
end end
end end
def self.recent_for_user user
where(project_id: user.projects.map(&:id)).recent
end end
# Next events currently enabled for system # Next events currently enabled for system
...@@ -46,10 +54,14 @@ class Event < ActiveRecord::Base ...@@ -46,10 +54,14 @@ class Event < ActiveRecord::Base
if project if project
project.name project.name
else else
"(deleted)" "(deleted project)"
end end
end end
def target_title
target.try :title
end
def push? def push?
action == self.class::Pushed && valid_push? action == self.class::Pushed && valid_push?
end end
...@@ -131,24 +143,21 @@ class Event < ActiveRecord::Base ...@@ -131,24 +143,21 @@ class Event < ActiveRecord::Base
"opened" "opened"
end end
end end
delegate :name, :email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true
end end
# == Schema Information # == Schema Information
# #
# Table name: events # Table name: events
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# target_type :string(255) # target_type :string(255)
# target_id :integer(4) # target_id :integer
# title :string(255) # title :string(255)
# data :text # data :text
# project_id :integer(4) # project_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# action :integer(4) # action :integer
# author_id :integer(4) # author_id :integer
# #
class Group < ActiveRecord::Base
attr_accessible :code, :name, :owner_id
has_many :projects
belongs_to :owner, class_name: "User"
validates :name, presence: true, uniqueness: true
validates :code, presence: true, uniqueness: true
validates :owner, presence: true
delegate :name, to: :owner, allow_nil: true, prefix: true
def self.search query
where("name LIKE :query OR code LIKE :query", query: "%#{query}%")
end
def to_param
code
end
def users
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
#
...@@ -2,49 +2,35 @@ class Issue < ActiveRecord::Base ...@@ -2,49 +2,35 @@ class Issue < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Votes include Votes
attr_accessible :title, :assignee_id, :closed, :position, :description,
:milestone_id, :label_list, :author_id_of_changes
acts_as_taggable_on :labels acts_as_taggable_on :labels
belongs_to :milestone belongs_to :milestone
validates :description, validates :description, length: { within: 0..2000 }
length: { within: 0..2000 }
def self.open_for(user) def self.open_for(user)
opened.assigned(user) opened.assigned(user)
end end
def is_assigned?
!!assignee_id
end
def is_being_reassigned?
assignee_id_changed?
end
def is_being_closed?
closed_changed? && closed
end
def is_being_reopened?
closed_changed? && !closed
end
end end
# == Schema Information # == Schema Information
# #
# Table name: issues # Table name: issues
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# title :string(255) # title :string(255)
# assignee_id :integer(4) # assignee_id :integer
# author_id :integer(4) # author_id :integer
# project_id :integer(4) # project_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# closed :boolean(1) default(FALSE), not null # closed :boolean default(FALSE), not null
# position :integer(4) default(0) # position :integer default(0)
# critical :boolean(1) default(FALSE), not null
# branch_name :string(255) # branch_name :string(255)
# description :text # description :text
# milestone_id :integer(4) # milestone_id :integer
# #
...@@ -4,21 +4,16 @@ class Key < ActiveRecord::Base ...@@ -4,21 +4,16 @@ class Key < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :project belongs_to :project
attr_protected :user_id attr_accessible :key, :title
validates :title, before_validation :strip_white_space
presence: true, before_save :set_identifier
length: { within: 0..255 }
validates :key, validates :title, presence: true, length: { within: 0..255 }
presence: true, validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }
format: { :with => /ssh-.{3} / }, validate :unique_key, :fingerprintable_key
length: { within: 0..5000 }
before_save :set_identifier
before_validation :strip_white_space
delegate :name, :email, to: :user, prefix: true delegate :name, :email, to: :user, prefix: true
validate :unique_key
def strip_white_space def strip_white_space
self.key = self.key.strip unless self.key.blank? self.key = self.key.strip unless self.key.blank?
...@@ -32,9 +27,24 @@ class Key < ActiveRecord::Base ...@@ -32,9 +27,24 @@ class Key < ActiveRecord::Base
end end
end end
def fingerprintable_key
return true unless key # Don't test if there is no key.
# `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected
file = Tempfile.new('key_file')
begin
file.puts key
file.rewind
fingerprint_output = `ssh-keygen -lf #{file.path} 2>&1` # Catch stderr.
ensure
file.close
file.unlink # deletes the temp file
end
errors.add(:key, "can't be fingerprinted") if fingerprint_output.match("failed")
end
def set_identifier def set_identifier
if is_deploy_key if is_deploy_key
self.identifier = "deploy_" + Digest::MD5.hexdigest(key) self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"
else else
self.identifier = "#{user.identifier}_#{Time.now.to_i}" self.identifier = "#{user.identifier}_#{Time.now.to_i}"
end end
...@@ -57,17 +67,18 @@ class Key < ActiveRecord::Base ...@@ -57,17 +67,18 @@ class Key < ActiveRecord::Base
Key.where(identifier: identifier).count == 0 Key.where(identifier: identifier).count == 0
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: keys # Table name: keys
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# user_id :integer(4) # user_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# key :text # key :text
# title :string(255) # title :string(255)
# identifier :string(255) # identifier :string(255)
# project_id :integer(4) # project_id :integer
# #
require File.join(Rails.root, "app/models/commit") require Rails.root.join("app/models/commit")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Votes include Votes
attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch,
:author_id_of_changes
attr_accessor :should_remove_source_branch
BROKEN_DIFF = "--broken-diff" BROKEN_DIFF = "--broken-diff"
UNCHECKED = 1 UNCHECKED = 1
...@@ -13,14 +18,12 @@ class MergeRequest < ActiveRecord::Base ...@@ -13,14 +18,12 @@ class MergeRequest < ActiveRecord::Base
serialize :st_commits serialize :st_commits
serialize :st_diffs serialize :st_diffs
attr_accessor :should_remove_source_branch validates :source_branch, presence: true
validates :target_branch, presence: true
validates_presence_of :source_branch
validates_presence_of :target_branch
validate :validate_branches validate :validate_branches
def self.find_all_by_branch(branch_name) def self.find_all_by_branch(branch_name)
where("source_branch like :branch or target_branch like :branch", branch: branch_name) where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
end end
def human_state def human_state
...@@ -48,7 +51,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -48,7 +51,8 @@ class MergeRequest < ActiveRecord::Base
end end
def mark_as_unchecked def mark_as_unchecked
self.update_attributes(state: UNCHECKED) self.state = UNCHECKED
self.save
end end
def can_be_merged? def can_be_merged?
...@@ -131,7 +135,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -131,7 +135,8 @@ class MergeRequest < ActiveRecord::Base
end end
def mark_as_unmergable def mark_as_unmergable
self.update_attributes state: CANNOT_BE_MERGED self.state = CANNOT_BE_MERGED
self.save
end end
def reloaded_commits def reloaded_commits
...@@ -162,7 +167,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -162,7 +167,7 @@ class MergeRequest < ActiveRecord::Base
end end
def automerge!(current_user) def automerge!(current_user)
if Gitlab::Merge.new(self, current_user).merge && self.unmerged_commits.empty? if Gitlab::Merge.new(self, current_user).merge! && self.unmerged_commits.empty?
self.merge!(current_user.id) self.merge!(current_user.id)
true true
end end
...@@ -182,24 +187,30 @@ class MergeRequest < ActiveRecord::Base ...@@ -182,24 +187,30 @@ class MergeRequest < ActiveRecord::Base
patch_path patch_path
end end
def mr_and_commit_notes
commit_ids = commits.map(&:id)
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 end
# == Schema Information # == Schema Information
# #
# Table name: merge_requests # Table name: merge_requests
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# target_branch :string(255) not null # target_branch :string(255) not null
# source_branch :string(255) not null # source_branch :string(255) not null
# project_id :integer(4) not null # project_id :integer not null
# author_id :integer(4) # author_id :integer
# assignee_id :integer(4) # assignee_id :integer
# title :string(255) # title :string(255)
# closed :boolean(1) default(FALSE), not null # closed :boolean default(FALSE), not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# st_commits :text(2147483647 # st_commits :text(4294967295
# st_diffs :text(2147483647 # st_diffs :text(4294967295
# merged :boolean(1) default(FALSE), not null # merged :boolean default(FALSE), not null
# state :integer(4) default(1), not null # state :integer default(1), not null
# #
# == Schema Information
#
# Table name: milestones
#
# id :integer(4) not null, primary key
# title :string(255) not null
# project_id :integer(4) not null
# description :text
# due_date :date
# closed :boolean(1) default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Milestone < ActiveRecord::Base class Milestone < ActiveRecord::Base
attr_accessible :title, :description, :due_date, :closed
belongs_to :project belongs_to :project
has_many :issues has_many :issues
validates_presence_of :project_id validates :title, presence: true
validates_presence_of :title validates :project, presence: true
def self.active def self.active
where("due_date > ? OR due_date IS NULL", Date.today) where("due_date > ? OR due_date IS NULL", Date.today)
end end
def participants def participants
User.where(id: issues.map(&:assignee_id)) User.where(id: issues.pluck(:assignee_id))
end end
def percent_complete def percent_complete
...@@ -37,3 +25,18 @@ class Milestone < ActiveRecord::Base ...@@ -37,3 +25,18 @@ class Milestone < ActiveRecord::Base
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
end end
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
#
...@@ -2,52 +2,42 @@ require 'carrierwave/orm/activerecord' ...@@ -2,52 +2,42 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator' require 'file_size_validator'
class Note < ActiveRecord::Base class Note < ActiveRecord::Base
belongs_to :project
belongs_to :noteable, polymorphic: true
belongs_to :author,
class_name: "User"
delegate :name, attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
to: :project, :attachment, :line_code
prefix: true
delegate :name,
:email,
to: :author,
prefix: true
attr_protected :author, :author_id
attr_accessor :notify attr_accessor :notify
attr_accessor :notify_author attr_accessor :notify_author
validates_presence_of :project belongs_to :project
belongs_to :noteable, polymorphic: true
belongs_to :author, class_name: "User"
validates :note, delegate :name, to: :project, prefix: true
presence: true, delegate :name, :email, to: :author, prefix: true
length: { within: 0..5000 }
validates :attachment, validates :project, presence: true
file_size: { validates :note, presence: true, length: { within: 0..5000 }
maximum: 10.megabytes.to_i validates :attachment, file_size: { maximum: 10.megabytes.to_i }
}
scope :common, where(noteable_id: nil) mount_uploader :attachment, AttachmentUploader
# Scopes
scope :common, where(noteable_id: nil)
scope :today, where("created_at >= :date", date: Date.today) scope :today, where("created_at >= :date", date: Date.today)
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days)) scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
scope :since, lambda { |day| where("created_at >= :date", date: (day)) } scope :since, ->(day) { where("created_at >= :date", date: (day)) }
scope :fresh, order("created_at ASC, id ASC") scope :fresh, order("created_at ASC, id ASC")
scope :inc_author_project, includes(:project, :author) scope :inc_author_project, includes(:project, :author)
scope :inc_author, includes(:author) scope :inc_author, includes(:author)
mount_uploader :attachment, AttachmentUploader
def self.create_status_change_note(noteable, author, status) def self.create_status_change_note(noteable, author, status)
create({ noteable: noteable, create({
noteable: noteable,
project: noteable.project, project: noteable.project,
author: author, author: author,
note: "_Status changed to #{status}_" }, note: "_Status changed to #{status}_"
without_protection: true) }, without_protection: true)
end end
def notify def notify
...@@ -58,11 +48,12 @@ class Note < ActiveRecord::Base ...@@ -58,11 +48,12 @@ class Note < ActiveRecord::Base
@notify_author ||= false @notify_author ||= false
end end
def target # override to return commits, which are not active record
if noteable_type == "Commit" def noteable
if for_commit?
project.commit(noteable_id) project.commit(noteable_id)
else else
noteable super
end end
# Temp fix to prevent app crash # Temp fix to prevent app crash
# if note commit id doesnt exist # if note commit id doesnt exist
...@@ -84,18 +75,22 @@ class Note < ActiveRecord::Base ...@@ -84,18 +75,22 @@ class Note < ActiveRecord::Base
# Boolean # Boolean
# #
def notify_only_author?(user) def notify_only_author?(user)
commit? && commit_author && for_commit? && commit_author &&
commit_author.email != user.email commit_author.email != user.email
end end
def commit? def for_commit?
noteable_type == "Commit" noteable_type == "Commit"
end end
def for_diff_line?
line_code.present?
end
def commit_author def commit_author
@commit_author ||= @commit_author ||=
project.users.find_by_email(target.author_email) || project.users.find_by_email(noteable.author_email) ||
project.users.find_by_name(target.author_name) project.users.find_by_name(noteable.author_name)
rescue rescue
nil nil
end end
...@@ -112,18 +107,19 @@ class Note < ActiveRecord::Base ...@@ -112,18 +107,19 @@ class Note < ActiveRecord::Base
note.start_with?('-1') || note.start_with?(':-1:') note.start_with?('-1') || note.start_with?(':-1:')
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: notes # Table name: notes
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# note :text # note :text
# noteable_id :string(255) # noteable_id :string(255)
# noteable_type :string(255) # noteable_type :string(255)
# author_id :integer(4) # author_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# project_id :integer(4) # project_id :integer
# attachment :string(255) # attachment :string(255)
# line_code :string(255) # line_code :string(255)
# #
......
...@@ -6,9 +6,12 @@ class Project < ActiveRecord::Base ...@@ -6,9 +6,12 @@ class Project < ActiveRecord::Base
include Authority include Authority
include Team include Team
# attr_accessible :name, :path, :description, :code, :default_branch, :issues_enabled,
:wall_enabled, :merge_requests_enabled, :wiki_enabled
attr_accessor :error_code
# Relations # Relations
# belongs_to :group
belongs_to :owner, class_name: "User" belongs_to :owner, class_name: "User"
has_many :users, through: :users_projects has_many :users, through: :users_projects
has_many :events, dependent: :destroy has_many :events, dependent: :destroy
...@@ -22,34 +25,43 @@ class Project < ActiveRecord::Base ...@@ -22,34 +25,43 @@ class Project < ActiveRecord::Base
has_many :hooks, dependent: :destroy, class_name: "ProjectHook" has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
has_many :wikis, dependent: :destroy has_many :wikis, dependent: :destroy
has_many :protected_branches, dependent: :destroy has_many :protected_branches, dependent: :destroy
has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
attr_accessor :error_code delegate :name, to: :owner, allow_nil: true, prefix: true
# # Validations
# Protected attributes validates :owner, presence: true
# validates :description, length: { within: 0..2000 }
attr_protected :private_flag, :owner_id validates :name, uniqueness: true, presence: true, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 0..255 },
format: { with: /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :code, presence: true, uniqueness: true, length: { within: 1..255 },
format: { with: /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validate :check_limit, :repo_name
#
# Scopes # Scopes
#
scope :public_only, where(private_flag: false) scope :public_only, where(private_flag: false)
scope :without_user, lambda { |user| where("id not in (:ids)", ids: user.projects.map(&:id) ) } scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) }
scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
def self.active class << self
def active
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC") joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
end end
def self.search query def search query
where("name like :query or code like :query or path like :query", query: "%#{query}%") where("name LIKE :query OR code LIKE :query OR path LIKE :query", query: "%#{query}%")
end end
def self.create_by_user(params, user) def create_by_user(params, user)
project = Project.new params project = Project.new params
Project.transaction do Project.transaction do
project.owner = user project.owner = user
project.save! project.save!
# Add user as project master # Add user as project master
...@@ -70,6 +82,11 @@ class Project < ActiveRecord::Base ...@@ -70,6 +82,11 @@ class Project < ActiveRecord::Base
project project
end end
def access_options
UsersProject.access_roles
end
end
def git_error? def git_error?
error_code == :gitolite error_code == :gitolite
end end
...@@ -78,37 +95,6 @@ class Project < ActiveRecord::Base ...@@ -78,37 +95,6 @@ class Project < ActiveRecord::Base
id && valid? id && valid?
end end
#
# Validations
#
validates :name,
uniqueness: true,
presence: true,
length: { within: 0..255 }
validates :path,
uniqueness: true,
presence: true,
format: { with: /^[a-zA-Z][a-zA-Z0-9_\-\.]*$/,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" },
length: { within: 0..255 }
validates :description,
length: { within: 0..2000 }
validates :code,
presence: true,
uniqueness: true,
format: { with: /^[a-zA-Z][a-zA-Z0-9_\-\.]*$/,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" },
length: { within: 1..255 }
validates :owner, presence: true
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validate :check_limit
validate :repo_name
def check_limit def check_limit
unless owner.can_create_project? unless owner.can_create_project?
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it") errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
...@@ -123,10 +109,6 @@ class Project < ActiveRecord::Base ...@@ -123,10 +109,6 @@ class Project < ActiveRecord::Base
end end
end end
def self.access_options
UsersProject.access_roles
end
def to_param def to_param
code code
end end
...@@ -148,7 +130,7 @@ class Project < ActiveRecord::Base ...@@ -148,7 +130,7 @@ class Project < ActiveRecord::Base
end end
def commit_line_notes(commit) def commit_line_notes(commit)
notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code is not null") notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
end end
def public? def public?
...@@ -160,43 +142,44 @@ class Project < ActiveRecord::Base ...@@ -160,43 +142,44 @@ class Project < ActiveRecord::Base
end end
def last_activity def last_activity
events.order("created_at ASC").last last_event
end end
def last_activity_date def last_activity_date
if events.last last_event.try(:created_at) || updated_at
events.last.created_at
else
updated_at
end
end end
def wiki_notes def wiki_notes
Note.where(noteable_id: wikis.map(&:id), noteable_type: 'Wiki', project_id: self.id) Note.where(noteable_id: wikis.pluck(:id), noteable_type: 'Wiki', project_id: self.id)
end end
def project_id def project_id
self.id self.id
end end
def issues_labels
issues.tag_counts_on(:labels)
end
end end
# == Schema Information # == Schema Information
# #
# Table name: projects # Table name: projects
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# name :string(255) # name :string(255)
# path :string(255) # path :string(255)
# description :text # description :text
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# private_flag :boolean(1) default(TRUE), not null # private_flag :boolean default(TRUE), not null
# code :string(255) # code :string(255)
# owner_id :integer(4) # owner_id :integer
# default_branch :string(255) # default_branch :string(255)
# issues_enabled :boolean(1) default(TRUE), not null # issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean(1) default(TRUE), not null # wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean(1) default(TRUE), not null # merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean(1) default(TRUE), not null # wiki_enabled :boolean default(TRUE), not null
# group_id :integer
# #
class ProjectHook < WebHook class ProjectHook < WebHook
belongs_to :project belongs_to :project
end end
# == Schema Information
#
# Table name: web_hooks
#
# id :integer not null, primary key
# url :string(255)
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255) default("ProjectHook")
#
class ProtectedBranch < ActiveRecord::Base class ProtectedBranch < ActiveRecord::Base
include GitHost include GitHost
attr_accessible :name
belongs_to :project belongs_to :project
validates_presence_of :project_id validates :name, presence: true
validates_presence_of :name validates :project, presence: true
after_save :update_repository after_save :update_repository
after_destroy :update_repository after_destroy :update_repository
...@@ -16,12 +18,13 @@ class ProtectedBranch < ActiveRecord::Base ...@@ -16,12 +18,13 @@ class ProtectedBranch < ActiveRecord::Base
project.commit(self.name) project.commit(self.name)
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: protected_branches # Table name: protected_branches
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# project_id :integer(4) not null # project_id :integer not null
# name :string(255) not null # name :string(255) not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
......
class Snippet < ActiveRecord::Base class Snippet < ActiveRecord::Base
include Linguist::BlobHelper include Linguist::BlobHelper
attr_accessible :title, :content, :file_name, :expires_at
belongs_to :project belongs_to :project
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
delegate :name, delegate :name, :email, to: :author, prefix: true
:email,
to: :author,
prefix: true
attr_protected :author, :author_id, :project, :project_id
validates_presence_of :project_id
validates_presence_of :author_id
validates :title,
presence: true,
length: { within: 0..255 }
validates :file_name, validates :author, presence: true
presence: true, validates :project, presence: true
length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
validates :file_name, presence: true, length: { within: 0..255 }
validates :content, validates :content, presence: true, length: { within: 0..10000 }
presence: true,
length: { within: 0..10000 }
# Scopes
scope :fresh, order("created_at DESC") scope :fresh, order("created_at DESC")
scope :non_expired, where(["expires_at IS NULL OR expires_at > ?", Time.current]) scope :non_expired, where(["expires_at IS NULL OR expires_at > ?", Time.current])
scope :expired, where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) scope :expired, where(["expires_at IS NOT NULL AND expires_at < ?", Time.current])
...@@ -58,15 +48,16 @@ class Snippet < ActiveRecord::Base ...@@ -58,15 +48,16 @@ class Snippet < ActiveRecord::Base
expires_at && expires_at < Time.current expires_at && expires_at < Time.current
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: snippets # Table name: snippets
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# title :string(255) # title :string(255)
# content :text # content :text
# author_id :integer(4) not null # author_id :integer not null
# project_id :integer(4) not null # project_id :integer not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# file_name :string(255) # file_name :string(255)
......
class SystemHook < WebHook class SystemHook < WebHook
def async_execute(data)
Resque.enqueue(SystemHookWorker, id, data)
end
def self.all_hooks_fire(data) def self.all_hooks_fire(data)
SystemHook.all.each do |sh| SystemHook.all.each do |sh|
sh.async_execute data sh.async_execute data
end end
end end
def async_execute(data)
Resque.enqueue(SystemHookWorker, id, data)
end
end end
# == Schema Information
#
# Table name: web_hooks
#
# id :integer not null, primary key
# url :string(255)
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255) default("ProjectHook")
#
...@@ -2,20 +2,12 @@ class Tree ...@@ -2,20 +2,12 @@ class Tree
include Linguist::BlobHelper include Linguist::BlobHelper
attr_accessor :path, :tree, :project, :ref attr_accessor :path, :tree, :project, :ref
delegate :contents, delegate :contents, :basename, :name, :data, :mime_type,
:basename, :mode, :size, :text?, :colorize, to: :tree
:name,
:data,
:mime_type,
:mode,
:size,
:text?,
:colorize,
to: :tree
def initialize(raw_tree, project, ref = nil, path = nil) def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path, @project, @ref, @path = project, ref, path
@tree = if path @tree = if path.present?
raw_tree / path.dup.force_encoding('ascii-8bit') raw_tree / path.dup.force_encoding('ascii-8bit')
else else
raw_tree raw_tree
...@@ -26,6 +18,10 @@ class Tree ...@@ -26,6 +18,10 @@ class Tree
tree.is_a?(Grit::Blob) tree.is_a?(Grit::Blob)
end end
def invalid?
tree.nil?
end
def empty? def empty?
data.blank? data.blank?
end end
......
class User < ActiveRecord::Base class User < ActiveRecord::Base
include Account include Account
devise :database_authenticatable, :token_authenticatable, :lockable, devise :database_authenticatable, :token_authenticatable, :lockable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable :recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, :skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password,
:theme_id, :force_random_password, :extern_uid, :provider :extern_uid, :provider, :as => [:default, :admin]
attr_accessible :projects_limit, :as => :admin
attr_accessor :force_random_password attr_accessor :force_random_password
has_many :users_projects, dependent: :destroy has_many :keys, dependent: :destroy
has_many :projects, through: :users_projects has_many :projects, through: :users_projects
has_many :users_projects, dependent: :destroy
has_many :issues, foreign_key: :author_id, dependent: :destroy
has_many :notes, foreign_key: :author_id, dependent: :destroy
has_many :merge_requests, foreign_key: :author_id, dependent: :destroy
has_many :my_own_projects, class_name: "Project", foreign_key: :owner_id has_many :my_own_projects, class_name: "Project", foreign_key: :owner_id
has_many :keys, dependent: :destroy has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy
has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
has_many :events, has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
class_name: "Event", has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
foreign_key: :author_id,
dependent: :destroy
has_many :recent_events,
class_name: "Event",
foreign_key: :author_id,
order: "id DESC"
has_many :issues,
foreign_key: :author_id,
dependent: :destroy
has_many :notes,
foreign_key: :author_id,
dependent: :destroy
has_many :assigned_issues,
class_name: "Issue",
foreign_key: :assignee_id,
dependent: :destroy
has_many :merge_requests,
foreign_key: :author_id,
dependent: :destroy
has_many :assigned_merge_requests,
class_name: "MergeRequest",
foreign_key: :assignee_id,
dependent: :destroy
validates :projects_limit,
presence: true,
numericality: {greater_than_or_equal_to: 0}
validates :bio, length: { within: 0..255 } validates :bio, length: { within: 0..255 }
validates :extern_uid, :allow_blank => true, :uniqueness => {:scope => :provider} validates :extern_uid, :allow_blank => true, :uniqueness => {:scope => :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
before_validation :generate_password, on: :create
before_save :ensure_authentication_token before_save :ensure_authentication_token
alias_attribute :private_token, :authentication_token alias_attribute :private_token, :authentication_token
scope :not_in_project, lambda { |project| where("id not in (:ids)", ids: project.users.map(&:id) ) } # Scopes
scope :not_in_project, ->(project) { where("id not in (:ids)", ids: project.users.map(&:id) ) }
scope :admins, where(admin: true) scope :admins, where(admin: true)
scope :blocked, where(blocked: true) scope :blocked, where(blocked: true)
scope :active, where(blocked: false) scope :active, where(blocked: false)
before_validation :generate_password, on: :create class << self
def filter filter_name
def generate_password
if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8)
end
end
def self.filter filter_name
case filter_name case filter_name
when "admins"; self.admins when "admins"; self.admins
when "blocked"; self.blocked when "blocked"; self.blocked
...@@ -82,41 +48,49 @@ class User < ActiveRecord::Base ...@@ -82,41 +48,49 @@ class User < ActiveRecord::Base
end end
end end
def self.without_projects def without_projects
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end end
def self.create_from_omniauth(auth, ldap = false) def create_from_omniauth(auth, ldap = false)
gitlab_auth.create_from_omniauth(auth, ldap) gitlab_auth.create_from_omniauth(auth, ldap)
end end
def self.find_or_new_for_omniauth(auth) def find_or_new_for_omniauth(auth)
gitlab_auth.find_or_new_for_omniauth(auth) gitlab_auth.find_or_new_for_omniauth(auth)
end end
def self.find_for_ldap_auth(auth, signed_in_resource = nil) def find_for_ldap_auth(auth, signed_in_resource = nil)
gitlab_auth.find_for_ldap_auth(auth, signed_in_resource) gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
end end
def self.gitlab_auth def gitlab_auth
Gitlab::Auth.new Gitlab::Auth.new
end end
def self.search query def search query
where("name like :query or email like :query", query: "%#{query}%") where("name LIKE :query or email LIKE :query", query: "%#{query}%")
end
end
def generate_password
if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8)
end
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: users # Table name: users
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# email :string(255) default(""), not null # email :string(255) default(""), not null
# encrypted_password :string(128) default(""), not null # encrypted_password :string(128) default(""), not null
# reset_password_token :string(255) # reset_password_token :string(255)
# reset_password_sent_at :datetime # reset_password_sent_at :datetime
# remember_created_at :datetime # remember_created_at :datetime
# sign_in_count :integer(4) default(0) # sign_in_count :integer default(0)
# current_sign_in_at :datetime # current_sign_in_at :datetime
# last_sign_in_at :datetime # last_sign_in_at :datetime
# current_sign_in_ip :string(255) # current_sign_in_ip :string(255)
...@@ -124,14 +98,19 @@ end ...@@ -124,14 +98,19 @@ end
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# name :string(255) # name :string(255)
# admin :boolean(1) default(FALSE), not null # admin :boolean default(FALSE), not null
# projects_limit :integer(4) default(10) # projects_limit :integer default(10)
# skype :string(255) default(""), not null # skype :string(255) default(""), not null
# linkedin :string(255) default(""), not null # linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null # twitter :string(255) default(""), not null
# authentication_token :string(255) # authentication_token :string(255)
# dark_scheme :boolean(1) default(FALSE), not null # dark_scheme :boolean default(FALSE), not null
# theme_id :integer(4) default(1), not null # theme_id :integer default(1), not null
# bio :string(255) # bio :string(255)
# blocked :boolean(1) default(FALSE), not null # blocked :boolean default(FALSE), not null
# failed_attempts :integer default(0)
# locked_at :datetime
# extern_uid :string(255)
# provider :string(255)
# #
...@@ -6,21 +6,22 @@ class UsersProject < ActiveRecord::Base ...@@ -6,21 +6,22 @@ class UsersProject < ActiveRecord::Base
DEVELOPER = 30 DEVELOPER = 30
MASTER = 40 MASTER = 40
attr_accessible :user, :user_id, :project_access
belongs_to :user belongs_to :user
belongs_to :project belongs_to :project
attr_protected :project_id, :project
after_save :update_repository after_save :update_repository
after_destroy :update_repository after_destroy :update_repository
validates_uniqueness_of :user_id, scope: [:project_id], message: "already exists in project" validates :user, presence: true
validates_presence_of :user_id validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" }
validates_presence_of :project_id validates :project, presence: true
delegate :name, :email, to: :user, prefix: true delegate :name, :email, to: :user, prefix: true
def self.bulk_delete(project, user_ids) class << self
def bulk_delete(project, user_ids)
UsersProject.transaction do UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.destroy users_project.destroy
...@@ -28,7 +29,7 @@ class UsersProject < ActiveRecord::Base ...@@ -28,7 +29,7 @@ class UsersProject < ActiveRecord::Base
end end
end end
def self.bulk_update(project, user_ids, project_access) def bulk_update(project, user_ids, project_access)
UsersProject.transaction do UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project| UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.project_access = project_access users_project.project_access = project_access
...@@ -37,7 +38,7 @@ class UsersProject < ActiveRecord::Base ...@@ -37,7 +38,7 @@ class UsersProject < ActiveRecord::Base
end end
end end
def self.bulk_import(project, user_ids, project_access) def bulk_import(project, user_ids, project_access)
UsersProject.transaction do UsersProject.transaction do
user_ids.each do |user_id| user_ids.each do |user_id|
users_project = UsersProject.new( users_project = UsersProject.new(
...@@ -50,7 +51,7 @@ class UsersProject < ActiveRecord::Base ...@@ -50,7 +51,7 @@ class UsersProject < ActiveRecord::Base
end end
end end
def self.user_bulk_import(user, project_ids, project_access) def user_bulk_import(user, project_ids, project_access)
UsersProject.transaction do UsersProject.transaction do
project_ids.each do |project_id| project_ids.each do |project_id|
users_project = UsersProject.new( users_project = UsersProject.new(
...@@ -63,7 +64,7 @@ class UsersProject < ActiveRecord::Base ...@@ -63,7 +64,7 @@ class UsersProject < ActiveRecord::Base
end end
end end
def self.access_roles def access_roles
{ {
"Guest" => GUEST, "Guest" => GUEST,
"Reporter" => REPORTER, "Reporter" => REPORTER,
...@@ -71,6 +72,7 @@ class UsersProject < ActiveRecord::Base ...@@ -71,6 +72,7 @@ class UsersProject < ActiveRecord::Base
"Master" => MASTER "Master" => MASTER
} }
end end
end
def role_access def role_access
project_access project_access
...@@ -88,15 +90,16 @@ class UsersProject < ActiveRecord::Base ...@@ -88,15 +90,16 @@ class UsersProject < ActiveRecord::Base
self.class.access_roles.invert[self.project_access] self.class.access_roles.invert[self.project_access]
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: users_projects # Table name: users_projects
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# user_id :integer(4) not null # user_id :integer not null
# project_id :integer(4) not null # project_id :integer not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# project_access :integer(4) default(0), not null # project_access :integer default(0), not null
# #
class WebHook < ActiveRecord::Base class WebHook < ActiveRecord::Base
include HTTParty include HTTParty
attr_accessible :url
# HTTParty timeout # HTTParty timeout
default_timeout 10 default_timeout 10
validates :url, validates :url, presence: true,
presence: true, format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }
format: {
with: URI::regexp(%w(http https)),
message: "should be a valid url" }
def execute(data) def execute(data)
parsed_url = URI.parse(url) parsed_url = URI.parse(url)
if parsed_url.userinfo.blank? if parsed_url.userinfo.blank?
WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
else else
post_url = url.gsub(parsed_url.userinfo+"@", "") post_url = url.gsub("#{parsed_url.userinfo}@", "")
WebHook.post(post_url, WebHook.post(post_url,
body: data.to_json, body: data.to_json,
headers: { "Content-Type" => "application/json" }, headers: {"Content-Type" => "application/json"},
basic_auth: {username: parsed_url.user, password: parsed_url.password}) basic_auth: {username: parsed_url.user, password: parsed_url.password})
end end
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: web_hooks # Table name: web_hooks
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# url :string(255) # url :string(255)
# project_id :integer(4) # project_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# type :string(255) default("ProjectHook")
# #
class Wiki < ActiveRecord::Base class Wiki < ActiveRecord::Base
attr_accessible :title, :content, :slug
belongs_to :project belongs_to :project
belongs_to :user belongs_to :user
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
validates :content, :title, :user_id, presence: true validates :content, presence: true
validates :title, length: 1..250 validates :user, presence: true
validates :title, presence: true, length: 1..250
before_update :set_slug before_update :set_slug
...@@ -14,12 +17,7 @@ class Wiki < ActiveRecord::Base ...@@ -14,12 +17,7 @@ class Wiki < ActiveRecord::Base
protected protected
def set_slug def self.regenerate_from wiki
self.slug = self.title.parameterize
end
class << self
def regenerate_from wiki
regenerated_field = [:slug, :content, :title] regenerated_field = [:slug, :content, :title]
new_wiki = Wiki.new new_wiki = Wiki.new
...@@ -28,19 +26,24 @@ class Wiki < ActiveRecord::Base ...@@ -28,19 +26,24 @@ class Wiki < ActiveRecord::Base
end end
new_wiki new_wiki
end end
def set_slug
self.slug = self.title.parameterize
end end
end end
# == Schema Information # == Schema Information
# #
# Table name: wikis # Table name: wikis
# #
# id :integer(4) not null, primary key # id :integer not null, primary key
# title :string(255) # title :string(255)
# content :text # content :text
# project_id :integer(4) # project_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# slug :string(255) # slug :string(255)
# user_id :integer(4) # user_id :integer
# #
class MailerObserver < ActiveRecord::Observer
observe :note, :merge_request
cattr_accessor :current_user
def after_create(model)
new_note(model) if model.kind_of?(Note)
new_merge_request(model) if model.kind_of?(MergeRequest)
end
def after_update(model)
changed_merge_request(model) if model.kind_of?(MergeRequest)
end
protected
def new_note(note)
if note.notify
# Notify whole team except author of note
notify_note(note)
elsif note.notify_author
# Notify only author of resource
Notify.note_commit_email(note.commit_author.id, note.id).deliver
else
# Otherwise ignore it
nil
end
end
def notify_note note
# reject author of note from mail list
users = note.project.users.reject { |u| u.id == current_user.id }
users.each do |u|
case note.noteable_type
when "Commit"; Notify.note_commit_email(u.id, note.id).deliver
when "Issue"; Notify.note_issue_email(u.id, note.id).deliver
when "Wiki"; Notify.note_wiki_email(u.id, note.id).deliver
when "MergeRequest"; Notify.note_merge_request_email(u.id, note.id).deliver
when "Snippet"; true
else
Notify.note_wall_email(u.id, note.id).deliver
end
end
end
def new_merge_request(merge_request)
if merge_request.assignee && merge_request.assignee != current_user
Notify.new_merge_request_email(merge_request.id).deliver
end
end
def changed_merge_request(merge_request)
status_notify_and_comment merge_request, :reassigned_merge_request_email
end
# This method used for Issues & Merge Requests
#
# It create a comment for Issue or MR if someone close/reopen.
# It also notify via email if assignee was changed
#
def status_notify_and_comment target, mail_method
# If assigne changed - notify to recipients
if target.assignee_id_changed?
recipients_ids = target.assignee_id_was, target.assignee_id
recipients_ids.delete current_user.id
recipients_ids.each do |recipient_id|
Notify.send(mail_method, recipient_id, target.id, target.assignee_id_was).deliver
end
end
# Create comment about status changed
if target.closed_changed?
note = Note.new(noteable: target, project: target.project)
note.author = current_user
note.note = "_Status changed to #{target.closed ? 'closed' : 'reopened'}_"
note.save()
end
end
end
class MergeRequestObserver < ActiveRecord::Observer
cattr_accessor :current_user
def after_create(merge_request)
if merge_request.assignee && merge_request.assignee != current_user
Notify.new_merge_request_email(merge_request.id).deliver
end
end
def after_update(merge_request)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
status = nil
status = 'closed' if merge_request.is_being_closed?
status = 'reopened' if merge_request.is_being_reopened?
if status
Note.create_status_change_note(merge_request, current_user, status)
end
end
protected
def send_reassigned_email(merge_request)
recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id
recipients_ids.delete current_user.id
recipients_ids.each do |recipient_id|
Notify.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was).deliver
end
end
end
class NoteObserver < ActiveRecord::Observer
def after_create(note)
send_notify_mails(note)
end
protected
def send_notify_mails(note)
if note.notify
notify_team(note)
elsif note.notify_author
# Notify only author of resource
Notify.note_commit_email(note.commit_author.id, note.id).deliver
else
# Otherwise ignore it
nil
end
end
# Notifies the whole team except the author of note
def notify_team(note)
# Note: wall posts are not "attached" to anything, so fall back to "Wall"
noteable_type = note.noteable_type || "Wall"
notify_method = "note_#{noteable_type.underscore}_email".to_sym
if Notify.respond_to? notify_method
team_without_note_author(note).map do |u|
Notify.send(notify_method, u.id, note.id).deliver
end
end
end
def team_without_note_author(note)
note.project.users.reject { |u| u.id == note.author.id }
end
end
class UsersProjectObserver < ActiveRecord::Observer class UsersProjectObserver < ActiveRecord::Observer
def after_create(users_project) def after_commit(users_project)
return if users_project.destroyed?
Notify.project_access_granted_email(users_project.id).deliver Notify.project_access_granted_email(users_project.id).deliver
end
def after_create(users_project)
Event.create( Event.create(
project_id: users_project.project.id, project_id: users_project.project.id,
action: Event::Joined, action: Event::Joined,
...@@ -9,10 +12,6 @@ class UsersProjectObserver < ActiveRecord::Observer ...@@ -9,10 +12,6 @@ class UsersProjectObserver < ActiveRecord::Observer
) )
end end
def after_update(users_project)
Notify.project_access_granted_email(users_project.id).deliver
end
def after_destroy(users_project) def after_destroy(users_project)
Event.create( Event.create(
project_id: users_project.project.id, project_id: users_project.project.id,
......
...@@ -22,6 +22,10 @@ module Account ...@@ -22,6 +22,10 @@ module Account
projects_limit > my_own_projects.count projects_limit > my_own_projects.count
end end
def can_create_group?
is_admin?
end
def last_activity_project def last_activity_project
projects.first projects.first
end end
...@@ -41,7 +45,7 @@ module Account ...@@ -41,7 +45,7 @@ module Account
# Remove user from all projects and # Remove user from all projects and
# set blocked attribute to true # set blocked attribute to true
def block def block
users_projects.all.each do |membership| users_projects.find_each do |membership|
return false unless membership.destroy return false unless membership.destroy
end end
......
...@@ -53,6 +53,6 @@ module Authority ...@@ -53,6 +53,6 @@ module Authority
end end
def master_access_for?(user) def master_access_for?(user)
!users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty? || owner_id == user.id !users_projects.where(user_id: user.id, project_access: [UsersProject::MASTER]).empty?
end end
end end
...@@ -3,24 +3,21 @@ module IssueCommonality ...@@ -3,24 +3,21 @@ module IssueCommonality
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
attr_protected :author, :author_id, :project, :project_id
belongs_to :project belongs_to :project
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
validates_presence_of :project_id validates :project, presence: true
validates_presence_of :author_id validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :title,
presence: true,
length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] } validates :closed, inclusion: { in: [true, false] }
scope :opened, where(closed: false) scope :opened, where(closed: false)
scope :closed, where(closed: true) scope :closed, where(closed: true)
scope :assigned, lambda { |u| where(assignee_id: u.id)} scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :assigned, ->(u) { where(assignee_id: u.id)}
scope :recent, order("created_at DESC")
delegate :name, delegate :name,
:email, :email,
...@@ -49,4 +46,21 @@ module IssueCommonality ...@@ -49,4 +46,21 @@ module IssueCommonality
def new? def new?
today? && created_at == updated_at today? && created_at == updated_at
end end
def is_assigned?
!!assignee_id
end
def is_being_reassigned?
assignee_id_changed?
end
def is_being_closed?
closed_changed? && closed
end
def is_being_reopened?
closed_changed? && !closed
end
end end
...@@ -70,7 +70,7 @@ module PushEvent ...@@ -70,7 +70,7 @@ module PushEvent
if new_ref? if new_ref?
"pushed new" "pushed new"
elsif rm_ref? elsif rm_ref?
"removed #{ref_type}" "deleted"
else else
"pushed to" "pushed to"
end end
......
...@@ -32,6 +32,10 @@ module Repository ...@@ -32,6 +32,10 @@ module Repository
Commit.commits(repo, ref, path, limit, offset) Commit.commits(repo, ref, path, limit, offset)
end end
def last_commit_for(ref, path = nil)
commits(ref, path, 1).first
end
def commits_between(from, to) def commits_between(from, to)
Commit.commits_between(repo, from, to) Commit.commits_between(repo, from, to)
end end
...@@ -45,8 +49,29 @@ module Repository ...@@ -45,8 +49,29 @@ module Repository
File.exists?(hook_file) File.exists?(hook_file)
end end
# Returns an Array of branch names
def branch_names
repo.branches.collect(&:name).sort
end
# Returns an Array of Branches
def branches
repo.branches.sort_by(&:name)
end
# Returns an Array of tag names
def tag_names
repo.tags.collect(&:name).sort.reverse
end
# Returns an Array of Tags
def tags def tags
repo.tags.map(&:name).sort.reverse repo.tags.sort_by(&:name).reverse
end
# Returns an Array of branch and tag names
def ref_names
[branch_names + tag_names].flatten
end end
def repo def repo
...@@ -79,14 +104,6 @@ module Repository ...@@ -79,14 +104,6 @@ module Repository
@heads ||= repo.heads @heads ||= repo.heads
end end
def branches_names
heads.map(&:name)
end
def ref_names
[branches_names + tags].flatten
end
def tree(fcommit, path = nil) def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head fcommit = commit if fcommit == :head
tree = fcommit.tree tree = fcommit.tree
...@@ -109,14 +126,12 @@ module Repository ...@@ -109,14 +126,12 @@ module Repository
# - If two or more branches are present, returns the one that has a name # - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil) # matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch def discover_default_branch
branches = heads.collect(&:name) if branch_names.length == 0
if branches.length == 0
nil nil
elsif branches.length == 1 elsif branch_names.length == 1
branches.first branch_names.first
else else
branches.select { |v| v == root_ref }.first branch_names.select { |v| v == root_ref }.first
end end
end end
...@@ -144,7 +159,7 @@ module Repository ...@@ -144,7 +159,7 @@ module Repository
# Build file path # Build file path
file_name = self.code + "-" + commit.id.to_s + ".tar.gz" file_name = self.code + "-" + commit.id.to_s + ".tar.gz"
storage_path = File.join(Rails.root, "tmp", "repositories", self.code) storage_path = Rails.root.join("tmp", "repositories", self.code)
file_path = File.join(storage_path, file_name) file_path = File.join(storage_path, file_name)
# Put files into a directory before archiving # Put files into a directory before archiving
...@@ -166,4 +181,9 @@ module Repository ...@@ -166,4 +181,9 @@ module Repository
def http_url_to_repo def http_url_to_repo
http_url = [Gitlab.config.url, "/", path, ".git"].join('') http_url = [Gitlab.config.url, "/", path, ".git"].join('')
end end
# Check if current branch name is marked as protected in the system
def protected_branch? branch_name
protected_branches.map(&:name).include?(branch_name)
end
end end
...@@ -25,6 +25,10 @@ module StaticModel ...@@ -25,6 +25,10 @@ module StaticModel
id id
end end
def new_record?
false
end
def persisted? def persisted?
false false
end end
...@@ -32,4 +36,12 @@ module StaticModel ...@@ -32,4 +36,12 @@ module StaticModel
def destroyed? def destroyed?
false false
end end
def ==(other)
if other.is_a? StaticModel
id == other.id
else
super
end
end
end end
module Team module Team
def team_member_by_name_or_email(email = nil, name = nil) def team_member_by_name_or_email(name = nil, email = nil)
user = users.where("email like ? or name like ?", email, name).first user = users.where("name like ? or email like ?", name, email).first
users_projects.find_by_user_id(user.id) if user users_projects.where(user: user) if user
end end
# Get Team Member record by user id # Get Team Member record by user id
......
= form_for [:admin, @group] do |f|
- if @group.errors.any?
.alert-message.block-message.error
%span= @group.errors.full_messages.first
.clearfix.group_name_holder
= f.label :name do
Group name is
.input
= f.text_field :name, placeholder: "Example Group", class: "xxlarge"
.clearfix
= f.label :code do
URL
.input
.input-prepend
%span.add-on= web_app_url + 'groups/'
= f.text_field :code, placeholder: "example"
.form-actions
= f.submit 'Save group', class: "btn save-btn"
%h3.page_title Edit Group
%br
= render 'form'
= render 'admin/shared/projects_head'
%h3.page_title
Groups
= link_to 'New Group', new_admin_group_path, class: "btn small right"
%br
= form_tag admin_groups_path, method: :get, class: 'form-inline' do
= text_field_tag :name, params[:name], class: "xlarge"
= submit_tag "Search", class: "btn submit primary"
%table
%thead
%th Name
%th Code
%th Projects
%th Edit
%th.cred Danger Zone!
- @groups.each do |group|
%tr
%td= link_to group.name, [:admin, group]
%td= group.code
%td= group.projects.count
%td= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small"
%td.bgred= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger"
= paginate @groups, theme: "admin"
%h3.page_title New Group
%br
= render 'form'
= render 'admin/shared/projects_head'
%h3.page_title
Group: #{@group.name}
= link_to edit_admin_group_path(@group), class: "btn right" do
%i.icon-edit
Edit
%br
%table.zebra-striped
%thead
%tr
%th Group
%th
%tr
%td
%b
Name:
%td
= @group.name
%tr
%td
%b
Code:
%td
= @group.code
%tr
%td
%b
Owner:
%td
= @group.owner_name
.ui-box
%h5
Projects
%small
(#{@group.projects.count})
%ul.unstyled
- @group.projects.each do |project|
%li.wll
%strong
= link_to project.name, [:admin, project]
.right
= link_to 'Remove from group', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
.clearfix
%br
%h3 Add new project
%br
= form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do
= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
.form-actions
= submit_tag 'Add', class: "btn primary"
= render 'admin/shared/projects_head'
%h3.page_title %h3.page_title
Projects Projects
= link_to 'New Project', new_admin_project_path, class: "btn small right" = link_to 'New Project', new_admin_project_path, class: "btn small right"
...@@ -11,7 +12,6 @@ ...@@ -11,7 +12,6 @@
%th Name %th Name
%th Path %th Path
%th Team Members %th Team Members
%th Post Receive
%th Last Commit %th Last Commit
%th Edit %th Edit
%th.cred Danger Zone! %th.cred Danger Zone!
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
%td= link_to project.name, [:admin, project] %td= link_to project.name, [:admin, project]
%td= project.path %td= project.path
%td= project.users_projects.count %td= project.users_projects.count
%td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, disabled: true
%td= last_commit(project) %td= last_commit(project)
%td= link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small" %td= link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn small"
%td.bgred= link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger" %td.bgred= link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn small danger"
......
%h3 = render 'admin/shared/projects_head'
= @admin_project.name %h3.page_title
= link_to 'Edit', edit_admin_project_path(@admin_project), class: "btn right small" Project: #{@admin_project.name}
= link_to edit_admin_project_path(@admin_project), class: "btn right" do
%i.icon-edit
Edit
- if !@admin_project.has_post_receive_file? && @admin_project.commit
%br
.alert.alert-error
%span
%strong Important!
Project has commits but missing post-receive file.
%br
If you exported project manually - copy post-receive hook to bare repository
%br %br
%table.zebra-striped %table.zebra-striped
...@@ -56,7 +68,7 @@ ...@@ -56,7 +68,7 @@
%tr %tr
%td %td
= link_to tm.user_name, admin_user_path(tm.user) = link_to tm.user_name, admin_user_path(tm.user)
%td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled %td= tm.project_access_human
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small" %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
......
%ul.nav.nav-tabs
= nav_link(controller: :projects) do
= link_to 'Projects', admin_projects_path, class: "tab"
= nav_link(controller: :groups) do
= link_to 'Groups', admin_groups_path, class: "tab"
%h3 %h3.page_title
= @admin_user.name User: #{@admin_user.name}
- if @admin_user.blocked - if @admin_user.blocked
%small Blocked %small Blocked
- if @admin_user.admin - if @admin_user.admin
%small Administrator %small Administrator
= link_to 'Edit', edit_admin_user_path(@admin_user), class: "btn small right" = link_to edit_admin_user_path(@admin_user), class: "btn right" do
%i.icon-edit
Edit
%br %br
...@@ -94,6 +96,6 @@ ...@@ -94,6 +96,6 @@
- project = tm.project - project = tm.project
%tr %tr
%td= link_to project.name, admin_project_path(project) %td= link_to project.name, admin_project_path(project)
%td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), class: "medium project-access-select", disabled: :disabled %td= tm.project_access_human
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li %li
= render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]} = render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]}
%li{class: "#{'active' if (controller.controller_name == "refs") }"} = nav_link(controller: :refs) do
= link_to tree_project_ref_path(@project, @ref) do = link_to 'Source', project_tree_path(@project, @ref)
Source
%li.right %li.right
.input-prepend.project_clone_holder .input-prepend.project_clone_holder
%button{class: "btn small active", :"data-clone" => @project.ssh_url_to_repo} SSH %button{class: "btn small active", :"data-clone" => @project.ssh_url_to_repo} SSH
%button{class: "btn small", :"data-clone" => @project.http_url_to_repo} HTTP %button{class: "btn small", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.web_protocol.upcase
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5" = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5"
= render "head" = render "head"
#tree-holder #tree-holder.tree-holder
%ul.breadcrumb %ul.breadcrumb
%li %li
%span.arrow %span.arrow
= link_to tree_project_ref_path(@project, @ref, path: nil) do = link_to project_tree_path(@project, @ref) do
= @project.name = @project.name
- @tree.breadcrumbs(6) do |link| - @tree.breadcrumbs(6) do |link|
\/ \/
...@@ -15,12 +15,9 @@ ...@@ -15,12 +15,9 @@
.file_title .file_title
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= @tree.name = @tree.name.force_encoding('utf-8')
%small blame %small= number_to_human_size @tree.size
%span.options %span.options= render "tree/blob_actions"
= link_to "raw", blob_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small", target: "_blank"
= link_to "history", project_commits_path(@project, path: params[:path], ref: @ref), class: "btn very_small"
= link_to "source", tree_file_project_ref_path(@project, @ref, path: params[:path]), class: "btn very_small"
.file_content.blame .file_content.blame
%table %table
- @blame.each do |commit, lines| - @blame.each do |commit, lines|
...@@ -32,8 +29,8 @@ ...@@ -32,8 +29,8 @@
= commit.author_name = commit.author_name
%td.blame_commit %td.blame_commit
&nbsp; &nbsp;
%code= link_to commit.short_id, project_commit_path(@project, id: commit.id) %code= link_to commit.short_id, project_commit_path(@project, commit)
= link_to_gfm truncate(commit.title, length: 30), project_commit_path(@project, id: commit.id), class: "row_title" rescue "--broken encoding" = link_to_gfm truncate(commit.title, length: 30), project_commit_path(@project, commit), class: "row_title" rescue "--broken encoding"
%td.lines %td.lines
= preserve do = preserve do
%pre %pre
......
= render "commits/commit_box"
= render "commits/diffs", diffs: @commit.diffs
= render "notes/notes_with_form", tid: @commit.id, tt: "commit"
= render "notes/per_line_form"
:javascript
$(function(){
PerLineNotes.init();
});
<%= @commit.to_patch %>
%li.commit %li.commit
.browse_code_link_holder .browse_code_link_holder
%p %p
%strong= link_to "Browse Code »", tree_project_ref_path(@project, commit.id), class: "right" %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
%p %p
= link_to commit.short_id(8), project_commit_path(@project, id: commit.id), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
%strong.commit-author-name= commit.author_name %strong.commit-author-name= commit.author_name
%span.dash &ndash; %span.dash &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, id: commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title"
%span.committed_ago %span.committed_ago
= time_ago_in_words(commit.committed_date) = time_ago_in_words(commit.committed_date)
ago ago
&nbsp; &nbsp;
%span.notes_count
- notes = @project.commit_notes(commit) + @project.commit_line_notes(commit)
- if notes.any?
%span.btn.small.disabled.grouped
%i.icon-comment
= notes.count
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
%span.btn.disabled.grouped %span.btn.disabled.grouped
%i.icon-comment %i.icon-comment
= @notes_count = @notes_count
= link_to patch_project_commit_path(@project, @commit.id), class: "btn small grouped" do = link_to project_commit_path(@project, @commit, format: :patch), class: "btn small grouped" do
%i.icon-download-alt %i.icon-download-alt
Get Patch Get Patch
= link_to tree_project_ref_path(@project, @commit.id), class: "browse-button primary grouped" do = link_to project_tree_path(@project, @commit), class: "browse-button primary grouped" do
%strong Browse Code » %strong Browse Code »
%h3.commit-title.page_title %h3.commit-title.page_title
= gfm escape_once(@commit.title) = gfm escape_once(@commit.title)
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
= gfm escape_once(@commit.description) = gfm escape_once(@commit.description)
.commit-info .commit-info
.row .row
.span4 .span5
= image_tag gravatar_icon(@commit.author_email, 40), class: "avatar" = image_tag gravatar_icon(@commit.author_email, 40), class: "avatar"
.author .author
%strong= @commit.author_name %strong= @commit.author_name
...@@ -31,10 +31,10 @@ ...@@ -31,10 +31,10 @@
committed committed
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")} %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.committed_date)} ago #{time_ago_in_words(@commit.committed_date)} ago
.span7.right .span6.right
.sha-block .sha-block
%span.cgray commit %span.cgray commit
%code= @commit.id %code.label_commit= @commit.id
.sha-block .sha-block
%span.cgray= pluralize(@commit.parents.count, "parent") %span.cgray= pluralize(@commit.parents.count, "parent")
- @commit.parents.each do |parent| - @commit.parents.each do |parent|
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%p To prevent performance issue we rejected diff information. %p To prevent performance issue we rejected diff information.
%p %p
But if you still want to see diff But if you still want to see diff
= link_to "click this link", project_commit_path(@project, @commit.id, force_show_diff: true), class: "dark" = link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "dark"
%p.cgray %p.cgray
Showing #{pluralize(diffs.count, "changed file")} Showing #{pluralize(diffs.count, "changed file")}
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
%i.icon-file %i.icon-file
%span{id: "#{diff.old_path}"}= diff.old_path %span{id: "#{diff.old_path}"}= diff.old_path
- else - else
= link_to tree_file_project_ref_path(@project, @commit.id, diff.new_path) do = link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)) do
%i.icon-file %i.icon-file
%span{id: "#{diff.new_path}"}= diff.new_path %span{id: "#{diff.new_path}"}= diff.new_path
%br/ %br/
......
%ul.nav.nav-tabs %ul.nav.nav-tabs
%li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'} %li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
= link_to project_commits_path(@project) do = nav_link(controller: [:commit, :commits]) do
Commits = link_to 'Commits', project_commits_path(@project, @project.root_ref)
%li{class: "#{'active' if current_page?(compare_project_commits_path(@project)) }"} = nav_link(controller: :compare) do
= link_to compare_project_commits_path(@project) do = link_to 'Compare', project_compare_index_path(@project)
Compare
%li{class: "#{branches_tab_class}"} = nav_link(html_options: {class: branches_tab_class}) do
= link_to project_repository_path(@project) do = link_to project_repository_path(@project) do
Branches Branches
%span.badge= @project.repo.branch_count %span.badge= @project.branches.length
%li{class: "#{'active' if current_page?(tags_project_repository_path(@project)) }"} = nav_link(controller: :repositories, action: :tags) do
= link_to tags_project_repository_path(@project) do = link_to tags_project_repository_path(@project) do
Tags Tags
%span.badge= @project.repo.tag_count %span.badge= @project.tags.length
- if current_page?(project_commits_path(@project)) && current_user.private_token - if current_controller?(:commits) && current_user.private_token
%li.right %li.right
%span.rss-icon %span.rss-icon
= link_to project_commits_path(@project, :atom, { private_token: current_user.private_token, ref: @ref }), title: "Feed" do = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed" do
= image_tag "rss_ui.png", title: "feed" = image_tag "rss_ui.png", title: "feed"
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%table{class: "#{'hide' if too_big}"} %table{class: "#{'hide' if too_big}"}
- each_diff_line(diff.diff.lines.to_a, index) do |line, type, line_code, line_new, line_old| - each_diff_line(diff.diff.lines.to_a, index) do |line, type, line_code, line_new, line_old|
%tr.line_holder %tr.line_holder{ id: line_code }
- if type == "match" - if type == "match"
%td.old_line= "..." %td.old_line= "..."
%td.new_line= "..." %td.new_line= "..."
......
= render "head"
- if params[:path]
%ul.breadcrumb
%li
%span.arrow
= link_to project_commits_path(@project) do
= @project.name
%span.divider
\/
%li
%a{href: "#"}= params[:path].split("/").join(" / ")
%div{id: dom_id(@project)}
#commits_list= render "commits"
.clear
.loading{ style: "display:none;"}
- if @commits.count == @limit
:javascript
$(function(){
CommitsList.init("#{@ref}", #{@limit});
});
xml.instruct! xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Recent commits to #{@project.name}:#{@ref}" xml.title "Recent commits to #{@project.name}:#{@ref}"
xml.link :href => project_commits_url(@project, :atom, :ref => @ref), :rel => "self", :type => "application/atom+xml" xml.link :href => project_commits_url(@project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml"
xml.link :href => project_commits_url(@project), :rel => "alternate", :type => "text/html" xml.link :href => project_commits_url(@project, @ref), :rel => "alternate", :type => "text/html"
xml.id project_commits_url(@project) xml.id project_commits_url(@project)
xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any? xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any?
......
= render "commits/commit_box" = render "head"
= render "commits/diffs", diffs: @commit.diffs
= render "notes/notes_with_form", tid: @commit.id, tt: "commit"
= render "notes/per_line_form"
- if @path.present?
%ul.breadcrumb
%li
%span.arrow
= link_to project_commits_path(@project) do
= @project.name
%span.divider
\/
%li
%a{href: "#"}= @path.split("/").join(" / ")
:javascript %div{id: dom_id(@project)}
#commits_list= render "commits"
.clear
.loading{ style: "display:none;"}
- if @commits.count == @limit
:javascript
$(function(){ $(function(){
PerLineNotes.init(); CommitsList.init("#{@ref}", #{@limit});
}); });
= render "head"
%h3.page_title
Compare View
%hr
%div %div
%p.slead %p.slead
Fill input field with commit id like Fill input field with commit id like
...@@ -14,7 +8,7 @@ ...@@ -14,7 +8,7 @@
%br %br
= form_tag compare_project_commits_path(@project), method: :get do = form_tag project_compare_index_path(@project), method: :post do
.clearfix .clearfix
= text_field_tag :from, params[:from], placeholder: "master", class: "xlarge" = text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
= "..." = "..."
...@@ -25,29 +19,14 @@ ...@@ -25,29 +19,14 @@
.actions .actions
= submit_tag "Compare", class: "btn primary wide commits-compare-btn" = submit_tag "Compare", class: "btn primary wide commits-compare-btn"
- if @commits.present?
%div.ui-box
%h5.small Commits (#{@commits.count})
%ul.unstyled= render @commits
- unless @diffs.empty?
%h4 Diff
= render "commits/diffs", diffs: @diffs
:javascript :javascript
$(function() { $(function() {
var availableTags = #{@project.ref_names.to_json}; var availableTags = #{@project.ref_names.to_json};
$("#from").autocomplete({ $("#from, #to").autocomplete({
source: availableTags,
minLength: 1
});
$("#to").autocomplete({
source: availableTags, source: availableTags,
minLength: 1 minLength: 1
}); });
disableButtonIfEmptyField('#to', '.commits-compare-btn'); disableButtonIfEmptyField('#to', '.commits-compare-btn');
}); });
= render "commits/head"
%h3.page_title
Compare View
%hr
= render "form"
= render "commits/head"
%h3.page_title
Compare View
%hr
= render "form"
- if @commits.present?
%div.ui-box
%h5.small Commits (#{@commits.count})
%ul.unstyled= render @commits
- unless @diffs.empty?
%h4 Diff
= render "commits/diffs", diffs: @diffs
.groups_box
%h5
Groups
%small
(#{groups.count})
- if current_user.can_create_group?
%span.right
= link_to new_admin_group_path, class: "btn very_small info" do
%i.icon-plus
New Group
%ul.unstyled
- groups.each do |group|
%li.wll
= link_to group_path(id: group.code), class: dom_class(group) do
%strong.group_name= truncate(group.name, length: 25)
%span.arrow
&rarr;
%span.last_activity
%strong Projects:
%span= group.projects.count
.projects_box
%h5
Projects
%small
(#{projects.total_count})
- if current_user.can_create_project?
%span.right
= link_to new_project_path, class: "btn very_small info" do
%i.icon-plus
New Project
%ul.unstyled
- projects.each do |project|
%li.wll
= link_to project_path(project), class: dom_class(project) do
%strong.project_name= truncate(project.name, length: 25)
%span.arrow
&rarr;
%span.last_activity
%strong Last activity:
%span= project_last_activity(project)
.bottom= paginate projects, theme: "gitlab"
...@@ -12,6 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear ...@@ -12,6 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.entry do xml.entry do
event_link = event.feed_url event_link = event.feed_url
event_title = event.feed_title event_title = event.feed_title
event_summary = event.feed_summary
xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
xml.link :href => event_link xml.link :href => event_link
...@@ -22,7 +23,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear ...@@ -22,7 +23,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.name event.author_name xml.name event.author_name
xml.email event.author_email xml.email event.author_email
end end
xml.summary event_title xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }
end end
end end
end end
......
...@@ -9,28 +9,9 @@ ...@@ -9,28 +9,9 @@
.loading.hide .loading.hide
.side .side
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
.projects_box - if @groups.present?
%h5 = render "groups", groups: @groups
Projects = render "projects", projects: @projects
%small
(#{@projects.total_count})
- if current_user.can_create_project?
%span.right
= link_to new_project_path, class: "btn very_small info" do
%i.icon-plus
New Project
%ul.unstyled
- @projects.each do |project|
%li.wll
= link_to project_path(project), class: dom_class(project) do
%strong.project_name= truncate(project.name, length: 25)
%span.arrow
&rarr;
%span.last_activity
%strong Last activity:
%span= project_last_activity(project)
.bottom= paginate @projects, theme: "gitlab"
%div %div
%span.rss-icon %span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
......
- commit = CommitDecorator.decorate(commit) - commit = CommitDecorator.decorate(commit)
%li.commit %li.commit
%p %p
= link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
%strong.cdark= commit.author_name %span= commit.author_name
&ndash; &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16 = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding" = gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
- if event.allowed? - if event.allowed?
- if event.issue? %div.event-item
.event_feed = event_image(event)
= render "events/event_issue", event: event = image_tag gravatar_icon(event.author_email), class: "avatar"
- elsif event.merge_request? - if event.push?
.event_feed = render "events/event/push", event: event
= render "events/event_merge_request", event: event - else
= render "events/event/common", event: event
- elsif event.push?
.event_feed .clearfix
= render "events/event_push", event: event %span.cgray.right
= time_ago_in_words(event.created_at)
- elsif event.membership_changed? ago.
.event_feed .clearfix
= render "events/event_membership_changed", event: event
%div{:xmlns => "http://www.w3.org/1999/xhtml"}
%p= simple_format issue.description
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
issue
= link_to project_issue_path(event.project, event.issue) do
%strong= truncate event.issue_title
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= image_tag gravatar_icon(event.author_email), class: "avatar" = image_tag gravatar_icon(event.author_email), class: "avatar"
%span You pushed to %span You pushed to
= event.ref_type = event.ref_type
= link_to project_commits_path(event.project, ref: event.ref_name) do = link_to project_commits_path(event.project, event.ref_name) do
%strong= truncate(event.ref_name, length: 28) %strong= truncate(event.ref_name, length: 28)
at at
%strong= link_to event.project.name, event.project %strong= link_to event.project.name, event.project
......
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
project
%strong= link_to event.project_name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
- if event.action_name == "merged"
.event_icon= image_tag "event_mr_merged.png"
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label{class: event.action_name}= event.action_name
merge request
= link_to project_merge_request_path(event.project, event.merge_request) do
%strong= truncate event.merge_request_title
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
%br
%span= event.merge_request.source_branch
&rarr;
%span= event.merge_request.target_branch
%div{:xmlns => "http://www.w3.org/1999/xhtml"}
- event.commits.first(15).each do |commit|
%p
%strong= commit.author_name
= link_to "(##{commit.short_id})", project_commit_path(event.project, :id => commit.id)
%i
at
= commit.committed_date.strftime("%Y-%m-%d %H:%M:%S")
%blockquote= simple_format(escape_once(commit.safe_message))
- if event.commits_count > 15
%p
%i
\... and
= pluralize(event.commits_count - 15, "more commit")
%div
.event_icon= image_tag "event_push.png"
= image_tag gravatar_icon(event.author_email), class: "avatar"
%strong #{event.author_name}
%span.event_label.pushed= event.push_action_name
= event.ref_type
= link_to project_commits_path(event.project, ref: event.ref_name) do
%strong= event.ref_name
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
- if event.push_with_commits?
- if event.commits_count > 1
= link_to compare_project_commits_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do
%strong #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]}
- project = event.project
%ul.unstyled.event_commits
- if event.commits_count > 3
- event.commits[0...2].each do |commit|
= render "events/commit", commit: commit, project: project
%li
%br
\... and #{event.commits_count - 2} more commits
- else
- event.commits.each do |commit|
= render "events/commit", commit: commit, project: project
.event-title
%span.author_name= link_to_author event
%span.event_label{class: event.action_name}= event_action_name(event)
- if event.target
= link_to [event.project, event.target] do
%strong= truncate event.target_title
- else
%strong= truncate event.target_title
at
- if event.project
= link_to_project event.project
- else
= event.project_name
.event-title
%span.author_name= link_to_author event
%span.event_label.pushed #{event.push_action_name} #{event.ref_type}
- if event.rm_ref?
%strong= event.ref_name
- else
= link_to project_commits_path(event.project, event.ref_name) do
%strong= event.ref_name
at
%strong= link_to event.project.name, event.project
- if event.push_with_commits?
- project = event.project
.event-body
%ul.unstyled.event_commits
- few_commits = event.commits[0...2]
- few_commits.each do |commit|
= render "events/commit", commit: commit, project: project
- if event.commits_count > 1
%li.commits-stat
- if event.commits_count > 2
%span ... and #{event.commits_count - 2} more commits.
= link_to project_compare_path(event.project, from: event.parent_commit.id, to: event.last_commit.id) do
%strong Compare &rarr; #{event.parent_commit.id[0..7]}...#{event.last_commit.id[0..7]}
.projects_box
%h5
Projects
%small
(#{projects.count})
%ul.unstyled
- projects.each do |project|
%li.wll
= link_to project_path(project), class: dom_class(project) do
%strong.project_name= truncate(project.name, length: 25)
%span.arrow
&rarr;
%span.last_activity
%strong Last activity:
%span= project_last_activity(project)
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@user.name} issues"
xml.link :href => dashboard_issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
xml.link :href => dashboard_issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
xml.id dashboard_issues_url(:private_token => @user.private_token)
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
xml.entry do
xml.id project_issue_url(issue.project, issue)
xml.link :href => project_issue_url(issue.project, issue)
xml.title truncate(issue.title, :length => 80)
xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
xml.author do |author|
xml.name issue.author_name
xml.email issue.author_email
end
xml.summary issue.title
end
end
end
%h3.page_title
Issues
%small (assigned to you)
%small.right #{@issues.total_count} issues
%br
.clearfix
- if @issues.any?
- @issues.group_by(&:project).each do |group|
%div.ui-box
- @project = group[0]
%h5= @project.name
%ul.unstyled.issues_table
- group[1].each do |issue|
= render(partial: 'issues/show', locals: {issue: issue})
%hr
= paginate @issues, theme: "gitlab"
- else
%h3.nothing_here_message Nothing to show here
%h3.page_title
Merge Requests
%small (authored by or assigned to you)
%small.right #{@merge_requests.total_count} merge requests
%br
- if @merge_requests.any?
- @merge_requests.group_by(&:project).each do |group|
%ul.unstyled.ui-box
- @project = group[0]
%h5= @project.name
- group[1].each do |merge_request|
= render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
%hr
= paginate @merge_requests, theme: "gitlab"
- else
%h3.nothing_here_message Nothing to show here
.ui-box
%h5
People
%small
(#{@users.size})
%ul.unstyled
- @users.each do |user|
%li.wll
= image_tag gravatar_icon(user.email, 16), class: "avatar s16"
%strong= user.name
%span.cgray= user.email
= form_tag search_group_path(@group), method: :get, class: 'form-inline' do |f|
.padded
= label_tag :search do
%strong Looking for
.input
= search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
= submit_tag 'Search', class: "btn primary wide"
- if params[:search].present?
%br
%h3
Search results
%small (#{@projects.count + @merge_requests.count + @issues.count})
%hr
.search_results
.row
.span6
%table
%thead
%tr
%th Projects
%tbody
- @projects.each do |project|
%tr
%td
= link_to project do
%strong.term= project.name
%small.cgray
last activity at
= project.last_activity_date.stamp("Aug 25, 2011")
- if @projects.blank?
%tr
%td
%h4.nothing_here_message No Projects
%br
%table
%thead
%tr
%th Merge Requests
%tbody
- @merge_requests.each do |merge_request|
%tr
%td
= link_to [merge_request.project, merge_request] do
%span.badge.badge-info ##{merge_request.id}
&ndash;
%strong.term= truncate merge_request.title, length: 50
%strong.right
%span.label= merge_request.project.name
- if @merge_requests.blank?
%tr
%td
%h4.nothing_here_message No Merge Requests
.span6
%table
%thead
%tr
%th Issues
%tbody
- @issues.each do |issue|
%tr
%td
= link_to [issue.project, issue] do
%span.badge.badge-info ##{issue.id}
&ndash;
%strong.term= truncate issue.title, length: 40
%strong.right
%span.label= issue.project.name
- if @issues.blank?
%tr
%td
%h4.nothing_here_message No Issues
:javascript
$(function() {
$(".search_results .term").highlight("#{params[:search]}");
})
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
xml.link :href => projects_url(:atom), :rel => "self", :type => "application/atom+xml"
xml.link :href => projects_url, :rel => "alternate", :type => "text/html"
xml.id projects_url
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
if event.allowed?
event = EventDecorator.decorate(event)
xml.entry do
event_link = event.feed_url
event_title = event.feed_title
xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
xml.link :href => event_link
xml.title truncate(event_title, :length => 80)
xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
xml.author do |author|
xml.name event.author_name
xml.email event.author_email
end
xml.summary event_title
end
end
end
end
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