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:
- DB=postgresql
- DB=mysql
before_install:
- sudo apt-get install libicu-dev -y
......@@ -11,6 +12,7 @@ rvm:
- 1.9.3
services:
- mysql
- postgresql
before_script:
- "cp config/database.yml.$DB config/database.yml"
- "cp config/gitlab.yml.example config/gitlab.yml"
......@@ -18,4 +20,4 @@ before_script:
- "bundle exec rake db:migrate RAILS_ENV=test"
- "bundle exec rake db:seed_fu RAILS_ENV=test"
- "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
- Fixed resque custom config init
......@@ -9,7 +31,7 @@ v 2.9.0
- restyled projects list on dashboard
- ssh keys validation to prevent gitolite crash
- send notifications if changed premission in project
- scss refactoring. gitlab_bootstrap/ dir
- scss refactoring. gitlab_bootstrap/ dir
- fix git push http body bigger than 112k problem
- list of labels page under issues tab
- API for milestones, keys
......@@ -113,7 +135,7 @@ v 2.1.0
v 2.0.0
- gitolite as main git host system
- merge requests
- project/repo access
- project/repo access
- link to commit/issue feed
- design tab
- improved email notifications
......@@ -147,7 +169,7 @@ v 1.1.0
- bugfix & code cleaning
v 1.0.2
- fixed bug with empty project
- fixed bug with empty project
- added adv validation for project path & code
- feature: issues can be sortable
- bugfix
......
......@@ -7,12 +7,12 @@ If you want to contribute to GitLab, follow this process:
3. Code
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 can be merged w/o problems
* It wont broke existing functionality
* Its a quality code
* It won't break existing functionality
* It's quality code
* We like it :)
## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
......
......@@ -11,8 +11,9 @@ end
gem "rails", "3.2.8"
# Supported DBs
gem "sqlite3"
gem "mysql2"
gem "sqlite3", :group => :sqlite
gem "mysql2", :group => :mysql
gem "pg", :group => :postgres
# Auth
gem "devise", "~> 2.1.0"
......@@ -23,12 +24,18 @@ gem 'omniauth-github'
# GITLAB patched libs
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 'yaml_db', :git => "https://github.com/gitlabhq/yaml_db.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
gem "grape", "~> 0.2.1"
......@@ -57,7 +64,7 @@ gem "seed-fu"
# Markdown to HTML
gem "redcarpet", "~> 2.1.1"
gem "github-markup", "~> 0.7.4"
gem "github-markup", "~> 0.7.4", require: 'github/markup'
# Servers
gem "thin"
......@@ -87,6 +94,7 @@ gem 'settingslogic'
# Misc
gem "foreman"
gem 'gemoji', require: 'emoji/railtie'
gem "git"
group :assets do
......@@ -96,11 +104,13 @@ group :assets do
gem "therubyracer"
gem 'chosen-rails'
gem 'jquery-atwho-rails', '0.1.6'
gem "jquery-rails", "2.0.2"
gem "jquery-ui-rails", "0.5.0"
gem "modernizr", "2.5.3"
gem "raphael-rails", "1.5.2"
gem 'bootstrap-sass', "2.0.4"
gem "font-awesome-sass-rails", "~> 2.0.0"
end
group :development do
......@@ -110,6 +120,7 @@ group :development do
end
group :development, :test do
gem 'rails-dev-tweaks'
gem 'spinach-rails'
gem "rspec-rails"
gem "capybara"
......@@ -137,8 +148,9 @@ group :test do
gem 'email_spec'
gem 'resque_spec'
gem "webmock"
gem 'test_after_commit'
end
group :production do
gem "gitlab_meta", '2.9'
gem "gitlab_meta", '3.0'
end
......@@ -4,15 +4,6 @@ GIT
specs:
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
remote: https://github.com/gitlabhq/grack.git
revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
......@@ -30,16 +21,6 @@ GIT
mime-types (~> 1.15)
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
remote: https://github.com/gitlabhq/omniauth-ldap.git
revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
......@@ -51,14 +32,6 @@ GIT
pyu-ruby-sasl (~> 0.0.3.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
remote: https://github.com/gitlabhq/yaml_db.git
revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd
......@@ -162,18 +135,32 @@ GEM
multipart-post (~> 1.1)
ffaker (1.14.0)
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)
thor (>= 0.13.6)
gemoji (1.1.1)
gherkin-ruby (0.2.1)
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)
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)
hashie (~> 1.2)
multi_json
multi_xml
rack
rack-mount
gratr19 (0.4.4.1)
growl (1.0.3)
guard (1.3.2)
listen (>= 0.4.2)
......@@ -189,7 +176,8 @@ GEM
activesupport (~> 3.0)
haml (~> 3.0)
railties (~> 3.0)
hashery (1.4.0)
hashery (1.5.0)
blankslate
hashie (1.2.0)
headless (0.3.1)
hike (1.2.1)
......@@ -199,6 +187,7 @@ GEM
httpauth (0.1)
i18n (0.6.1)
journey (1.0.4)
jquery-atwho-rails (0.1.6)
jquery-rails (2.0.2)
railties (>= 3.2.0, < 5.0)
thor (~> 0.14)
......@@ -260,12 +249,16 @@ GEM
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
orm_adapter (0.3.0)
pg (0.14.0)
polyglot (0.3.3)
posix-spawn (0.3.6)
pry (0.9.9.6)
coderay (~> 1.0.5)
method_source (~> 0.7.1)
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)
rack (1.4.1)
rack-cache (1.2)
......@@ -288,6 +281,9 @@ GEM
activesupport (= 3.2.8)
bundler (~> 1.0)
railties (= 3.2.8)
rails-dev-tweaks (0.6.1)
actionpack (~> 3.1)
railties (~> 3.1)
railties (3.2.8)
actionpack (= 3.2.8)
activesupport (= 3.2.8)
......@@ -332,9 +328,6 @@ GEM
railties (>= 3.0)
rspec (~> 2.10.0)
rubyntlm (0.1.1)
rubypython (0.6.2)
blankslate (>= 2.1.2.3)
ffi (~> 1.0.7)
rubyzip (0.9.8)
sass (3.1.19)
sass-rails (3.2.5)
......@@ -376,6 +369,7 @@ GEM
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.6)
stamp (0.1.6)
test_after_commit (0.0.1)
therubyracer (0.10.1)
libv8 (~> 3.3.10)
thin (1.3.1)
......@@ -404,6 +398,7 @@ GEM
crack (>= 0.1.7)
xpath (0.1.4)
nokogiri (~> 1.3)
yajl-ruby (1.1.0)
PLATFORMS
ruby
......@@ -426,11 +421,14 @@ DEPENDENCIES
email_spec
factory_girl_rails
ffaker
font-awesome-sass-rails (~> 2.0.0)
foreman
gemoji
git
github-linguist (~> 2.3.4)
github-markup (~> 0.7.4)
gitlab_meta (= 2.9)
gitolite!
gitlab_meta (= 3.0)
gitolite (= 1.1.0)
grack!
grape (~> 0.2.1)
grit!
......@@ -440,12 +438,12 @@ DEPENDENCIES
haml-rails
headless
httparty
jquery-atwho-rails (= 0.1.6)
jquery-rails (= 2.0.2)
jquery-ui-rails (= 0.5.0)
kaminari
launchy
letter_opener
linguist (~> 1.0.0)!
modernizr (= 2.5.3)
mysql2
omniauth
......@@ -453,10 +451,12 @@ DEPENDENCIES
omniauth-google-oauth2
omniauth-ldap!
omniauth-twitter
pg
pry
pygments.rb!
pygments.rb (= 0.3.1)
rack-mini-profiler
rails (= 3.2.8)
rails-dev-tweaks
raphael-rails (= 1.5.2)
rb-fsevent
rb-inotify
......@@ -474,6 +474,7 @@ DEPENDENCIES
spinach-rails
sqlite3
stamp
test_after_commit
therubyracer
thin
uglifier (= 1.0.3)
......
......@@ -12,7 +12,7 @@ GitLab is a free project and repository management application
## Requirements
* Ubuntu/Debian
* ruby 1.9.2+
* ruby 1.9.3+
* mysql or sqlite
* git
* gitolite
......
......@@ -10,10 +10,13 @@
//= require jquery.cookie
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.history
//= require jquery.waitforimages
//= require jquery.atwho
//= require bootstrap
//= require modernizr
//= require chosen-jquery
//= require raphael
//= require branch-graph
//= require ace-src-noconflict/ace
//= 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){
$("#new_issue_dialog").show("fade", { direction: "right" }, 150);
$('.top-tabs .add_new').hide();
disableButtonIfEmptyField("#issue_title", ".save-btn");
setupGfmAutoComplete();
});
}
......@@ -17,6 +18,7 @@ function switchToEditIssue(form){
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
$('.add_new').hide();
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 = {
updateVotes:
function() {
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
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 @@
* 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.aristo
*= require jquery.atwho
*= require chosen
*= require_self
*= require main
......
......@@ -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 {
@extend .form-horizontal;
......@@ -355,41 +325,6 @@ p.time {
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 {
background: url("images.png") no-repeat -85px -77px;
......@@ -639,22 +574,6 @@ li.note {
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,
.mr_show_all_commits {
......@@ -742,3 +661,12 @@ li.note {
margin-right: 30px;
}
}
pre {
&.clean {
background:none;
border:none;
margin:0;
padding:0;
}
}
......@@ -46,6 +46,9 @@
color:#888;
text-shadow:0 1px 1px #fff;
}
i[class^="icon-"] {
line-height:14px;
}
}
&.active {
> a {
......
......@@ -68,10 +68,22 @@
* Blame file
*/
&.blame {
table {
border:none;
box-shadow:none;
margin:0;
}
tr {
border-bottom: 1px solid #eee;
}
td {
&:first-child {
border-left:none;
}
&:last-child {
border-right:none;
}
background:#fff;
padding:5px;
}
.author,
......
/** LISTS **/
ul {
ul {
/**
* List li block element #1
*
......@@ -13,12 +13,15 @@ ul {
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
&.smoke { background-color:#f5f5f5; }
&:hover { background:$hover; }
&:hover {
background:$hover;
border-bottom:1px solid #ADF;
}
&:last-child { border:none }
.author { color: #999; }
p {
padding-top:5px;
padding-top:5px;
margin:0;
color:#222;
img {
......
......@@ -11,6 +11,11 @@ table {
border-bottom: 1px solid #bbb;
text-shadow: 0 1px 1px #fff;
@include bg-dark-gray-gradient;
ul.nav {
text-shadow: none;
margin: 0;
}
}
th, td {
......
@import "bootstrap";
@import "bootstrap-responsive";
@import 'font-awesome';
/** GitLab colors **/
$link_color:#3A89A3;
$blue_link: #2fa0bb;
$style_color: #474d57;
$hover: #fdf5d9;
$hover: #D9EDF7;
/** 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 **/
@mixin shade {
......@@ -143,6 +144,7 @@ $hover: #fdf5d9;
@import "sections/projects.scss";
@import "sections/merge_requests.scss";
@import "sections/graph.scss";
@import "sections/events.scss";
/**
* This scss file redefine chozen selectbox styles for
......@@ -181,3 +183,9 @@ $hover: #fdf5d9;
*
*/
@import "highlight/dark.scss";
/**
* File Editor styles
*
*/
@import "sections/editor.scss";
.commit-box {
@extend .main_box;
.commit-head {
.commit-head {
@extend .top_box_content;
.commit-title {
......@@ -29,11 +29,11 @@
.sha-block {
text-align:right;
&:first-child {
&:first-child {
padding-bottom:6px;
}
a {
a {
border-bottom: 1px solid #aaa;
margin-left: 9px;
}
......@@ -47,6 +47,14 @@
padding-left: 32px;
}
.author,
.committer {
font-size:14px;
line-height:22px;
text-shadow:0 1px 1px #fff;
color:#777;
}
.avatar {
margin-right: 10px;
}
......@@ -54,7 +62,7 @@
}
/**
*
*
* COMMIT SHOw
*
*/
......@@ -71,7 +79,7 @@
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
span {
span {
font-size:14px;
}
}
......@@ -111,8 +119,8 @@
}
}
&.img_compared {
img {
&.img_compared {
img {
max-width:300px;
}
}
......@@ -120,12 +128,12 @@
}
.diff_file_content{
table {
table {
border:none;
margin:0px;
padding:0px;
tr {
td {
td {
font-size:12px;
}
}
......@@ -145,29 +153,29 @@
moz-user-select: none;
-khtml-user-select: none;
user-select: none;
a {
a {
float:left;
width:35px;
font-weight:normal;
color:#666;
&:hover {
&:hover {
text-decoration:underline;
}
}
}
.line_content {
white-space:pre;
.line_content {
white-space:pre;
height:14px;
margin:0px;
padding:0px;
border:none;
&.new {
&.new {
background: #CFD;
}
&.old {
&.old {
background: #FDD;
}
&.matched {
&.matched {
color:#ccc;
background:#fafafa;
}
......@@ -182,32 +190,37 @@
/** COMMIT ROW **/
.commit {
.commit {
@extend .wll;
.browse_code_link_holder {
.browse_code_link_holder {
@extend .span2;
float:right;
}
.committed_ago {
.committed_ago {
float:right;
@extend .cgray;
}
code {
.notes_count {
float:right;
margin: -6px 8px 6px;
}
code {
background:#FCEEC1;
color:$style_color;
}
.commit_short_id {
.commit_short_id {
float:left;
@extend .lined;
min-width:65px;
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
}
.commit-author-name {
.commit-author-name {
color: #777;
}
}
......@@ -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 {
}
}
.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: 1000px) { .issues_filters select { width:200px; } }
@media (min-width: 1200px) { .issues_filters select { width:220px; } }
......@@ -113,3 +130,17 @@ input.check_all_issues {
.milestone {
@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 {
border-left: 0;
}
&.current {
&.active {
background-color:#D5D5D5;
border-right: 1px solid #BBB;
border-left: 1px solid #BBB;
......
......@@ -43,7 +43,9 @@
padding: 8px 0;
overflow: hidden;
display: block;
position:relative;
img {float: left; margin-right: 10px;}
img.emoji {float:none;margin:0;}
.note-author cite{font-style: italic;}
p { color:$style_color; }
.note-author { color: $style_color;}
......@@ -55,7 +57,9 @@
.delete-note {
display:none;
float:right;
position:absolute;
right:0;
top:0;
}
&:hover {
......
......@@ -7,18 +7,25 @@
@extend .span4;
@extend .right;
.groups_box,
.projects_box {
h5 {
color:$style_color;
font-size:16px;
text-shadow: 0 1px 1px #fff;
padding: 2px 10px;
line-height:32px;
font-size:14px;
}
ul {
li {
padding:0;
a {
display:block;
.group_name {
font-size:14px;
line-height:18px;
}
.project_name {
color:#4fa2bd;
font-size:14px;
......@@ -74,7 +81,7 @@
@include bg-gray-gradient;
padding: 4px 7px;
border: 1px solid #CCC;
margin-bottom:5px;
margin-bottom:20px;
}
.project_clone_holder {
......
#tree-holder {
#tree-content-holder {
.tree-holder {
.tree-content-holder {
float:left;
width:100%;
}
#tree-readme-holder {
float:left;
width:100%;
.readme {
border:1px solid #ccc;
padding:12px;
background: #F7F7F7;
pre {
overflow: auto;
}
}
}
.tree_progress {
display:none;
......@@ -25,14 +12,14 @@
}
}
#tree-slider {
.tree-table {
@include border-radius(0);
.tree-item {
&:hover {
td {
background: $hover;
border-top:1px solid #FEA;
border-bottom:1px solid #FEA;
border-top:1px solid #ADF;
border-bottom:1px solid #ADF;
}
cursor:pointer;
}
......@@ -55,22 +42,44 @@
}
}
#tree-slider {
.tree-table {
th .btn {
margin: -2px -1px;
padding: 2px 10px;
}
td {
background:#fafafa;
}
}
.tree-commit-link {
color:#333;
.tree_author {
padding-right: 8px;
img.avatar {
border: 0 none;
float: none;
margin-right: 0;
padding: 0;
width: 16px;
}
}
a.tree-commit-link {
color: #666;
&:hover {
text-decoration: underline;
.tree_commit {
color: gray;
.tree-commit-link {
color: #444;
&:hover {
text-decoration: underline;
}
}
}
}
.tree-btn-group {
.btn {
margin-right:-3px;
padding:2px 10px;
}
}
class CommitLoad < BaseContext
class CommitLoadContext < BaseContext
def execute
result = {
result = {
commit: nil,
suppress_diff: false,
line_notes: [],
notes_count: 0,
note: nil,
note: nil,
status: :ok
}
commit = project.commit(params[:id])
if commit
if commit
commit = CommitDecorator.decorate(commit)
line_notes = project.commit_line_notes(commit)
......
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
type = params[:f]
......
......@@ -13,7 +13,7 @@ module Notes
when "issue"
project.issues.find(target_id).notes.inc_author.fresh.limit(20)
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"
project.snippets.find(target_id).notes.fresh
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
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
def edit
......@@ -60,7 +60,7 @@ class Admin::UsersController < AdminController
def create
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)
respond_to do |format|
......@@ -86,7 +86,7 @@ class Admin::UsersController < AdminController
@admin_user.admin = (admin && admin.to_i > 0)
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.json { head :ok }
else
......
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
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 :dev_tools if Rails.env == 'development'
......@@ -11,28 +9,19 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can?
rescue_from Gitlab::Gitolite::AccessDenied do |exception|
render "errors/gitolite", layout: "error", status: 500
render "errors/gitolite", layout: "errors", status: 500
end
rescue_from Encoding::CompatibilityError do |exception|
render "errors/encoding", layout: "error", status: 500
render "errors/encoding", layout: "errors", status: 500
end
rescue_from ActiveRecord::RecordNotFound do |exception|
render "errors/not_found", layout: "error", status: 404
render "errors/not_found", layout: "errors", status: 404
end
layout :layout_by_resource
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!
if current_user && current_user.blocked
sign_out current_user
......@@ -51,19 +40,8 @@ class ApplicationController < ActionController::Base
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
MergeRequestObserver.current_user = current_user
IssueObserver.current_user = current_user
end
......@@ -76,7 +54,7 @@ class ApplicationController < ActionController::Base
end
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
end
......@@ -93,15 +71,15 @@ class ApplicationController < ActionController::Base
end
def access_denied!
render "errors/access_denied", layout: "error", status: 404
render "errors/access_denied", layout: "errors", status: 404
end
def not_found!
render "errors/not_found", layout: "error", status: 404
render "errors/not_found", layout: "errors", status: 404
end
def git_not_found!
render "errors/git_not_found", layout: "error", status: 404
render "errors/git_not_found", layout: "errors", status: 404
end
def method_missing(method_sym, *arguments, &block)
......@@ -113,7 +91,7 @@ class ApplicationController < ActionController::Base
end
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
def require_non_empty_project
......@@ -126,10 +104,6 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end
def render_full_content
@full_content = true
end
def dev_tools
Rack::MiniProfiler.authorize_request
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"
class CommitsController < ApplicationController
before_filter :project
layout "project"
class CommitsController < ProjectResourceController
include ExtractsPath
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :load_refs, only: :index # load @branch, @tag & @ref
before_filter :render_full_content
def index
@repo = project.repo
def show
@repo = @project.repo
@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)
respond_to do |format|
......@@ -25,54 +21,4 @@ class CommitsController < ApplicationController
format.atom { render layout: false }
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
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
respond_to :html
def index
@projects = current_user.projects_with_events.page(params[:page]).per(40)
@events = Event.recent_for_user(current_user).limit(20).offset(params[:offset] || 0)
@groups = Group.where(id: current_user.projects.pluck(:group_id))
@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
respond_to do |format|
......@@ -16,14 +19,14 @@ class DashboardController < ApplicationController
# Get authored or assigned open merge requests
def merge_requests
@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
# Get only assigned issues
def issues
@projects = current_user.projects.all
@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)
respond_to do |format|
......
class DeployKeysController < ApplicationController
class DeployKeysController < ProjectResourceController
respond_to :html
layout "project"
before_filter :project
# Authorize
before_filter :add_project_abilities
before_filter :authorize_admin_project!
def project
@project ||= Project.find_by_code(params[:project_id])
end
def index
@keys = @project.deploy_keys.all
end
......
class ErrorsController < ApplicationController
layout "error"
def githost
render "errors/gitolite"
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
before_filter :authenticate_user!
before_filter :project
layout "project"
class HooksController < ProjectResourceController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_admin_project!, only: [:new, :create, :destroy]
......
class IssuesController < ApplicationController
before_filter :authenticate_user!
before_filter :project
class IssuesController < ProjectResourceController
before_filter :module_enabled
before_filter :issue, only: [:edit, :update, :destroy, :show]
helper_method :issues_filter
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any issue
before_filter :authorize_read_issue!
......@@ -26,7 +18,6 @@ class IssuesController < ApplicationController
def index
@issues = issues_filtered
@issues = @issues.page(params[:page]).per(20)
respond_to do |format|
......@@ -61,7 +52,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.html do
if @issue.valid?
if @issue.valid?
redirect_to project_issue_path(@project, @issue)
else
render :new
......@@ -76,7 +67,7 @@ class IssuesController < ApplicationController
respond_to do |format|
format.js
format.html do
format.html do
if @issue.valid?
redirect_to [@project, @issue]
else
......@@ -141,35 +132,6 @@ class IssuesController < ApplicationController
end
def issues_filtered
@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
def issues_filter
{
all: "all",
closed: "closed",
to_me: "assigned-to-me",
open: "open"
}
@issues = IssuesListContext.new(project, current_user, params).execute
end
end
class LabelsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
class LabelsController < ProjectResourceController
before_filter :module_enabled
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any issue
before_filter :authorize_read_issue!
respond_to :js, :html
def index
@labels = @project.issues.tag_counts_on(:labels).order('count DESC')
@labels = @project.issues_labels.order('count DESC')
end
protected
......
class MergeRequestsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
class MergeRequestsController < ProjectResourceController
before_filter :module_enabled
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 :define_show_vars, only: [:show, :diffs]
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any merge_request
before_filter :authorize_read_merge_request!
......@@ -24,7 +18,7 @@ class MergeRequestsController < ApplicationController
def index
@merge_requests = MergeRequestsLoad.new(project, current_user, params).execute
@merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute
end
def show
......@@ -61,7 +55,7 @@ class MergeRequestsController < ApplicationController
@merge_request.reload_code
redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.'
else
render action: "new"
render action: "new"
end
end
......@@ -76,7 +70,7 @@ class MergeRequestsController < ApplicationController
end
def automerge_check
if @merge_request.unchecked?
if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
end
render json: {state: @merge_request.human_state}
......@@ -131,7 +125,7 @@ class MergeRequestsController < ApplicationController
def validates_merge_request
# Show git not found page if target branch doesnt exist
return git_not_found! unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch)
return git_not_found! unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch)
# Show git not found page if source branch doesnt exist
# and there is no saved commits between source & target branch
......@@ -142,7 +136,7 @@ class MergeRequestsController < ApplicationController
# Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request)
# Get commits from repository
# Get commits from repository
# or from cache if already merged
@commits = @merge_request.commits
@commits = CommitDecorator.decorate(@commits)
......
class MilestonesController < ApplicationController
before_filter :authenticate_user!
before_filter :project
class MilestonesController < ProjectResourceController
before_filter :module_enabled
before_filter :milestone, only: [:edit, :update, :destroy, :show]
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any milestone
before_filter :authorize_read_milestone!
......@@ -36,7 +30,7 @@ class MilestonesController < ApplicationController
end
def show
@issues = @milestone.issues.opened.page(params[:page]).per(40)
@issues = @milestone.issues
@users = @milestone.participants
respond_to do |format|
......@@ -60,7 +54,7 @@ class MilestonesController < ApplicationController
respond_to do |format|
format.js
format.html do
format.html do
if @milestone.valid?
redirect_to [@project, @milestone]
else
......
class NotesController < ApplicationController
before_filter :project
class NotesController < ProjectResourceController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_note!
before_filter :authorize_write_note!, only: [:create]
......@@ -11,6 +7,11 @@ class NotesController < ApplicationController
def index
notes
if params[:target_type] == "merge_request"
@mixed_targets = true
@main_target_type = params[:target_type].camelize
end
respond_with(@notes)
end
......
class ProfileController < ApplicationController
layout "profile"
before_filter :user
def show
......
class ProjectResourceController < ApplicationController
before_filter :project
# Authorize
before_filter :add_project_abilities
end
require Rails.root.join('lib', 'gitlab', 'graph_commit')
class ProjectsController < ApplicationController
before_filter :project, except: [:index, :new, :create]
layout :determine_layout
class ProjectsController < ProjectResourceController
skip_before_filter :project, only: [:new, :create]
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!, except: [:index, :new, :create]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'application', only: [:new, :create]
def new
@project = Project.new
end
......@@ -46,7 +46,7 @@ class ProjectsController < ApplicationController
def show
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|
format.html do
......@@ -57,6 +57,7 @@ class ProjectsController < ApplicationController
render "projects/empty"
end
end
format.js
end
end
......@@ -92,19 +93,4 @@ class ProjectsController < ApplicationController
format.html { redirect_to root_path }
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
class ProtectedBranchesController < ApplicationController
before_filter :project
class ProtectedBranchesController < ProjectResourceController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :require_non_empty_project
before_filter :authorize_admin_project!, only: [:destroy, :create]
before_filter :render_full_content
layout "project"
def index
@branches = @project.protected_branches.all
......
require 'github/markup'
class RefsController < ApplicationController
class RefsController < ProjectResourceController
include Gitlab::Encode
before_filter :project
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :ref
before_filter :define_tree_vars, only: [:tree, :blob, :blame, :logs_tree]
before_filter :render_full_content
layout "project"
before_filter :define_tree_vars, only: [:blob, :logs_tree]
def switch
respond_to do |format|
format.html do
def switch
respond_to do |format|
format.html do
new_path = if params[:destination] == "tree"
tree_project_ref_path(@project, params[:ref])
project_tree_path(@project, @ref)
else
project_commits_path(@project, ref: params[:ref])
project_commits_path(@project, @ref)
end
redirect_to new_path
redirect_to new_path
end
format.js do
format.js do
@ref = params[:ref]
define_tree_vars
render "tree"
......@@ -35,19 +28,6 @@ class RefsController < ApplicationController
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
contents = @tree.contents
@logs = contents.map do |content|
......@@ -55,36 +35,12 @@ class RefsController < ApplicationController
last_commit = @project.commits(@commit.id, file, 1).last
last_commit = CommitDecorator.decorate(last_commit)
{
file_name: content.name,
file_name: content.name,
commit: last_commit
}
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
def define_tree_vars
......@@ -95,20 +51,18 @@ class RefsController < ApplicationController
@commit = CommitDecorator.decorate(@commit)
@tree = Tree.new(@commit.tree, project, @ref, params[:path])
@tree = TreeDecorator.new(@tree)
@hex_path = Digest::SHA1.hexdigest(params[:path] || "/")
@hex_path = Digest::SHA1.hexdigest(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
@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
rescue
return render_404
end
def ref
@ref = params[:id]
@ref = params[:id] || params[:ref]
end
end
class RepositoriesController < ApplicationController
before_filter :project
class RepositoriesController < ProjectResourceController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_code_access!
before_filter :require_non_empty_project
before_filter :render_full_content
layout "project"
def show
@activities = @project.commits_with_refs(20)
end
def branches
@branches = @project.repo.heads.sort_by(&:name)
@branches = @project.branches
end
def tags
@tags = @project.repo.tags.sort_by(&:name).reverse
@tags = @project.tags
end
def archive
......
class SearchController < ApplicationController
def show
query = params[:search]
result = SearchContext.new(current_user.project_ids, params).execute
@projects = []
@merge_requests = []
@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
@projects = result[:projects]
@merge_requests = result[:merge_requests]
@issues = result[:issues]
end
end
class SnippetsController < ApplicationController
before_filter :authenticate_user!
before_filter :project
class SnippetsController < ProjectResourceController
before_filter :snippet, only: [:show, :edit, :destroy, :update, :raw]
layout "project"
# Authorize
before_filter :add_project_abilities
# Allow read any snippet
before_filter :authorize_read_snippet!
......@@ -56,7 +50,6 @@ class SnippetsController < ApplicationController
def show
@note = @project.notes.new(noteable: @snippet)
render_full_content
end
def destroy
......
class TeamMembersController < ApplicationController
before_filter :project
layout "project"
class TeamMembersController < ProjectResourceController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
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
before_filter :project
before_filter :add_project_abilities
class WikisController < ProjectResourceController
before_filter :authorize_read_wiki!
before_filter :authorize_write_wiki!, only: [:edit, :create, :history]
before_filter :authorize_admin_wiki!, only: :destroy
layout "project"
def pages
@wikis = @project.wikis.group(:slug).order("created_at")
......
......@@ -16,13 +16,15 @@ class CommitDecorator < ApplicationDecorator
# In case this first line is longer than 80 characters, it is cut off
# after 70 characters and ellipses (`&hellp;`) are appended.
def title
return no_commit_message if safe_message.blank?
title = safe_message
title_end = safe_message.index(/\n/)
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
safe_message[0..69] << "&hellip;".html_safe
return no_commit_message if title.blank?
title_end = title.index(/\n/)
if (!title_end && title.length > 80) || (title_end && title_end > 80)
title[0..69] << "&hellip;".html_safe
else
safe_message.split(/\n/, 2).first
title.split(/\n/, 2).first
end
end
......@@ -30,11 +32,35 @@ class CommitDecorator < ApplicationDecorator
#
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
def description
title_end = safe_message.index(/\n/)
if (!title_end && safe_message.length > 80) || (title_end && title_end > 80)
"&hellip;".html_safe << safe_message[70..-1]
description = safe_message
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
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
......
......@@ -3,11 +3,11 @@ class EventDecorator < ApplicationDecorator
def feed_title
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?
"#{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?
"#{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?
"#{self.author_name} #{self.action_name} #{self.project.name}"
else
......@@ -20,8 +20,25 @@ class EventDecorator < ApplicationDecorator
h.project_issue_url(self.project, self.issue)
elsif 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?
h.project_commits_url(self.project, ref: self.ref_name)
h.render "events/event_push", event: self
end
end
end
......@@ -6,39 +6,30 @@ class TreeDecorator < ApplicationDecorator
part_path = ""
parts = path.split("\/")
#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|
part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links
yield(h.link_to(h.truncate(part, length: 40), h.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
def up_dir?
!!path
path.present?
end
def up_dir_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
def history_path
h.project_commits_path(project, path: path, ref: ref)
end
def mb_size
size = (tree.size / 1024)
if size < 1024
"#{size} KB"
else
"#{size/1024} MB"
end
def readme
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
end
end
require 'digest/md5'
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)
if Gitlab.config.disable_gravatar? || user_email.blank?
'no_avatar.png'
......@@ -31,8 +60,8 @@ module ApplicationHelper
def grouped_options_refs(destination = :tree)
options = [
["Branch", @project.repo.heads.map(&:name) ],
[ "Tag", @project.tags ]
["Branch", @project.branch_names ],
[ "Tag", @project.tag_names ]
]
# If reference is commit id -
......@@ -47,26 +76,46 @@ module ApplicationHelper
def search_autocomplete_source
projects = current_user.projects.map{ |p| { label: p.name, url: project_path(p) } }
default_nav = [
{ label: "Profile", url: profile_path },
{ label: "Keys", url: keys_path },
{ label: "Dashboard", url: root_path },
{ label: "Admin", url: admin_root_path }
{ label: "My Profile", url: profile_path },
{ label: "My SSH Keys", url: keys_path },
{ label: "My Dashboard", url: 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?
project_nav = [
{ label: "#{@project.name} / Issues", url: project_issues_path(@project) },
{ label: "#{@project.name} / Wall", url: wall_project_path(@project) },
{ label: "#{@project.name} / Tree", url: tree_project_ref_path(@project, @project.root_ref) },
{ label: "#{@project.name} / Commits", url: project_commits_path(@project) },
{ label: "#{@project.name} / Team", url: project_team_index_path(@project) }
{ label: "#{@project.name} Issues", url: project_issues_path(@project) },
{ label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.root_ref) },
{ label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) },
{ label: "#{@project.name} Milestones", url: project_milestones_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
[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
def ldap_enable?
......@@ -85,45 +134,6 @@ module ApplicationHelper
event.project.merge_requests_enabled
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)
Digest::SHA1.hexdigest string
end
......
......@@ -17,7 +17,7 @@ module CommitsHelper
line_old = 1
line_new = 1
type = nil
lines_arr = ::Gitlab::InlineDiff.processing diff_arr
lines_arr.each do |line|
next if line.match(/^\-\-\- \/dev\/null/)
......
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
......@@ -33,7 +33,7 @@ module IssuesHelper
classes
end
def issue_tags
def issue_tags
@project.issues.tag_counts_on(:labels).map(&:name)
end
......@@ -43,4 +43,13 @@ module IssuesHelper
# Milestone uses :title, Issue uses :name
OpenStruct.new(id: 0, title: 'Unspecified', name: 'Unassigned')
end
def issues_filter
{
all: "all",
closed: "closed",
to_me: "assigned-to-me",
open: "open"
}
end
end
......@@ -24,7 +24,7 @@ module MergeRequestsHelper
def new_mr_path_from_push_event(event)
new_project_merge_request_path(
event.project,
merge_request: {
merge_request: {
source_branch: event.branch_name,
target_branch: event.project.root_ref,
title: event.branch_name.titleize
......
......@@ -7,11 +7,18 @@ module NotesHelper
params[:loading_new].present?
end
def note_vote_class(note)
if note.upvote?
"vote upvote"
elsif note.downvote?
"vote downvote"
end
# Helps to distinguish e.g. commit notes in mr notes list
def note_for_main_target?(note)
!@mixed_targets || @main_target_type == note.noteable_type
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
......@@ -6,5 +6,9 @@ module ProjectsHelper
def remove_from_team_message(project, member)
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
end
def link_to_project project
link_to project.name, project
end
end
module TabHelper
def issues_tab?
controller.controller_name == "issues" || controller.controller_name == "milestones"
end
# Navigation link helper
#
# 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
if c && a
# 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
# Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key
o = options.delete(:html_options) || {}
o[:class] ||= ''
o[:class] += ' ' + klass
o[:class].strip!
def wall_tab?
current_page?(controller: "projects", action: "wall", id: @project)
if block_given?
content_tag(:li, capture(&block), o)
else
content_tag(:li, nil, o)
end
end
def project_tab_class
[:show, :files, :edit, :update].each do |action|
return "current" if current_page?(controller: "projects", action: action, id: @project)
return "active" if current_page?(controller: "projects", action: action, id: @project)
end
if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
"current"
end
end
def tree_tab_class
controller.controller_name == "refs" ? "current" : nil
end
def commit_tab_class
if ['commits', 'repositories', 'protected_branches'].include? controller.controller_name
"current"
"active"
end
end
def branches_tab_class
if current_page?(branches_project_repository_path(@project)) ||
controller.controller_name == "protected_branches" ||
current_controller?(:protected_branches) ||
current_page?(project_repository_path(@project))
'active'
end
......
module TreeHelper
def tree_icon(content)
if content.is_a?(Grit::Blob)
if content.text?
image_tag "file_txt.png"
elsif content.image?
image_tag "file_img.png"
# Sorts a repository's tree so that folders are before files and renders
# their corresponding partials
#
# contents - A Grit::Tree object for the current tree
def render_tree(contents)
# Render Folders before Files/Submodules
folders, files = contents.partition { |v| v.kind_of?(Grit::Tree) }
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
image_tag "file_bin.png"
# Object is a Blob
tree += render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
end
else
image_tag "file_dir.png"
end
tree.html_safe
end
def tree_hex_class(content)
"file_#{hexdigest(content.name)}"
# Return an image icon depending on the file type
#
# type - String type of the tree item; either 'folder' or 'file'
def tree_icon(type)
image = type == 'folder' ? 'file_dir.png' : 'file_txt.png'
image_tag(image, size: '16x16')
end
def tree_full_path(content)
content.name.force_encoding('utf-8')
if params[:path]
File.join(params[:path], content.name)
else
content.name
end
def tree_hex_class(content)
"file_#{hexdigest(content.name)}"
end
# Public: Determines if a given filename is compatible with GitHub::Markup.
......@@ -39,4 +50,21 @@ module TreeHelper
def gitlab_markdown?(filename)
filename.end_with?(*%w(.mdown .md .markdown))
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
......@@ -9,11 +9,11 @@ class Notify < ActionMailer::Base
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"))
end
#
# Issue
#
def new_issue_email(issue_id)
@issue = Issue.find(issue_id)
......@@ -21,20 +21,61 @@ class Notify < ActionMailer::Base
mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title))
end
def note_wall_email(recipient_id, note_id)
@note = Note.find(note_id)
@project = @note.project
mail(to: recipient(recipient_id), subject: subject)
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
@issue = Issue.find(issue_id)
@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 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
#
# Note
#
def note_commit_email(recipient_id, note_id)
@note = Note.find(note_id)
@commit = @note.target
@commit = @note.noteable
@commit = CommitDecorator.decorate(@commit)
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
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)
@note = Note.find(note_id)
@merge_request = @note.noteable
......@@ -42,11 +83,10 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}"))
end
def note_issue_email(recipient_id, note_id)
def note_wall_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}"))
mail(to: recipient(recipient_id), subject: subject)
end
def note_wiki_email(recipient_id, note_id)
......@@ -56,25 +96,11 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject("note for wiki"))
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)
@previous_assignee ||= User.find(previous_assignee_id)
@project = @issue.project
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
end
#
# Project
#
def project_access_granted_email(user_project_id)
@users_project = UsersProject.find user_project_id
......@@ -83,14 +109,19 @@ class Notify < ActionMailer::Base
subject: subject("access to project was granted"))
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.id}", @issue.title))
#
# User
#
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
private
# Look up a User by their ID and return their email address
......
class Ability
def self.allowed(object, subject)
case subject.class.name
when "Project" then project_abilities(object, subject)
when "Issue" then issue_abilities(object, subject)
when "Note" then note_abilities(object, subject)
when "Snippet" then snippet_abilities(object, subject)
when "MergeRequest" then merge_request_abilities(object, subject)
else []
class << self
def allowed(object, subject)
case subject.class.name
when "Project" then project_abilities(object, subject)
when "Issue" then issue_abilities(object, subject)
when "Note" then note_abilities(object, subject)
when "Snippet" then snippet_abilities(object, subject)
when "MergeRequest" then merge_request_abilities(object, subject)
else []
end
end
end
def self.project_abilities(user, project)
rules = []
def project_abilities(user, project)
rules = []
rules << [
:read_project,
:read_wiki,
:read_issue,
:read_milestone,
:read_snippet,
:read_team_member,
:read_merge_request,
:read_note,
:write_project,
:write_issue,
:write_note
] if project.guest_access_for?(user)
rules << [
:read_project,
:read_wiki,
:read_issue,
:read_milestone,
:read_snippet,
:read_team_member,
:read_merge_request,
:read_note,
:write_project,
:write_issue,
:write_note
] if project.guest_access_for?(user)
rules << [
:download_code,
:write_merge_request,
:write_snippet
] if project.report_access_for?(user)
rules << [
:download_code,
:write_merge_request,
:write_snippet
] if project.report_access_for?(user)
rules << [
:write_wiki
] if project.dev_access_for?(user)
rules << [
:write_wiki,
:push_code
] if project.dev_access_for?(user)
rules << [
:modify_issue,
:modify_snippet,
:modify_merge_request,
:admin_project,
:admin_issue,
:admin_milestone,
:admin_snippet,
:admin_team_member,
:admin_merge_request,
:admin_note,
:accept_mr,
:admin_wiki
] if project.master_access_for?(user) || project.owner == user
rules << [
:push_code_to_protected_branches
] if project.master_access_for?(user)
rules << [
:modify_issue,
:modify_snippet,
:modify_merge_request,
:admin_project,
:admin_issue,
:admin_milestone,
:admin_snippet,
:admin_team_member,
:admin_merge_request,
:admin_note,
:accept_mr,
:admin_wiki
] if project.master_access_for?(user) || project.owner == user
rules.flatten
end
rules.flatten
end
class << self
[:issue, :note, :snippet, :merge_request].each do |name|
define_method "#{name}_abilities" do |user, subject|
if subject.author == user
......@@ -73,8 +77,7 @@ class Ability
:"modify_#{name}",
]
else
subject.respond_to?(:project) ?
project_abilities(user, subject.project) : []
subject.respond_to?(:project) ? project_abilities(user, subject.project) : []
end
end
end
......
......@@ -4,24 +4,11 @@ class Commit
include StaticModel
extend ActiveModel::Naming
attr_accessor :commit
attr_accessor :head
attr_accessor :refs
delegate :message,
:authored_date,
:committed_date,
:parents,
:sha,
:date,
:committer,
:author,
:message,
:diffs,
:tree,
:id,
:to_patch,
to: :commit
attr_accessor :commit, :head, :refs
delegate :message, :authored_date, :committed_date, :parents, :sha,
:date, :committer, :author, :message, :diffs, :tree, :id,
:to_patch, to: :commit
class << self
def find_or_first(repo, commit_id = nil, root_ref)
......@@ -30,6 +17,7 @@ class Commit
else
repo.commits(root_ref).first
end
Commit.new(commit) if commit
end
......@@ -119,7 +107,7 @@ class Commit
end
def safe_message
utf8 message
@safe_message ||= utf8 message
end
def created_at
......
class Event < ActiveRecord::Base
include PushEvent
attr_accessible :project, :action, :data, :author_id, :project_id,
:target_id, :target_type
default_scope where("author_id IS NOT NULL")
Created = 1
......@@ -13,27 +16,32 @@ class Event < ActiveRecord::Base
Joined = 8 # User joined 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 :target, polymorphic: true
# For Hash only
serialize :data
# Scopes
scope :recent, order("created_at DESC")
scope :code_push, where(action: Pushed)
def self.determine_action(record)
if [Issue, MergeRequest].include? record.class
Event::Created
elsif record.kind_of? Note
Event::Commented
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
class << self
def determine_action(record)
if [Issue, MergeRequest].include? record.class
Event::Created
elsif record.kind_of? Note
Event::Commented
end
end
end
def self.recent_for_user user
where(project_id: user.projects.map(&:id)).recent
end
# Next events currently enabled for system
# - push
# - new issue
......@@ -46,10 +54,14 @@ class Event < ActiveRecord::Base
if project
project.name
else
"(deleted)"
"(deleted project)"
end
end
def target_title
target.try :title
end
def push?
action == self.class::Pushed && valid_push?
end
......@@ -131,24 +143,21 @@ class Event < ActiveRecord::Base
"opened"
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
# == Schema Information
#
# Table name: events
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# target_type :string(255)
# target_id :integer(4)
# target_id :integer
# title :string(255)
# data :text
# project_id :integer(4)
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# action :integer(4)
# author_id :integer(4)
# action :integer
# 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
include IssueCommonality
include Votes
attr_accessible :title, :assignee_id, :closed, :position, :description,
:milestone_id, :label_list, :author_id_of_changes
acts_as_taggable_on :labels
belongs_to :milestone
validates :description,
length: { within: 0..2000 }
validates :description, length: { within: 0..2000 }
def self.open_for(user)
opened.assigned(user)
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
# == Schema Information
#
# Table name: issues
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# title :string(255)
# assignee_id :integer(4)
# author_id :integer(4)
# project_id :integer(4)
# assignee_id :integer
# author_id :integer
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# closed :boolean(1) default(FALSE), not null
# position :integer(4) default(0)
# critical :boolean(1) default(FALSE), not null
# closed :boolean default(FALSE), not null
# position :integer default(0)
# branch_name :string(255)
# description :text
# milestone_id :integer(4)
# milestone_id :integer
#
......@@ -4,21 +4,16 @@ class Key < ActiveRecord::Base
belongs_to :user
belongs_to :project
attr_protected :user_id
attr_accessible :key, :title
validates :title,
presence: true,
length: { within: 0..255 }
before_validation :strip_white_space
before_save :set_identifier
validates :key,
presence: true,
format: { :with => /ssh-.{3} / },
length: { within: 0..5000 }
validates :title, presence: true, length: { within: 0..255 }
validates :key, presence: true, length: { within: 0..5000 }, format: { :with => /ssh-.{3} / }
validate :unique_key, :fingerprintable_key
before_save :set_identifier
before_validation :strip_white_space
delegate :name, :email, to: :user, prefix: true
validate :unique_key
def strip_white_space
self.key = self.key.strip unless self.key.blank?
......@@ -32,9 +27,24 @@ class Key < ActiveRecord::Base
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
if is_deploy_key
self.identifier = "deploy_" + Digest::MD5.hexdigest(key)
self.identifier = "deploy_#{Digest::MD5.hexdigest(key)}"
else
self.identifier = "#{user.identifier}_#{Time.now.to_i}"
end
......@@ -57,17 +67,18 @@ class Key < ActiveRecord::Base
Key.where(identifier: identifier).count == 0
end
end
# == Schema Information
#
# Table name: keys
#
# id :integer(4) not null, primary key
# user_id :integer(4)
# id :integer not null, primary key
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# key :text
# title :string(255)
# identifier :string(255)
# project_id :integer(4)
# project_id :integer
#
require File.join(Rails.root, "app/models/commit")
require Rails.root.join("app/models/commit")
class MergeRequest < ActiveRecord::Base
include IssueCommonality
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"
UNCHECKED = 1
......@@ -13,14 +18,12 @@ class MergeRequest < ActiveRecord::Base
serialize :st_commits
serialize :st_diffs
attr_accessor :should_remove_source_branch
validates_presence_of :source_branch
validates_presence_of :target_branch
validates :source_branch, presence: true
validates :target_branch, presence: true
validate :validate_branches
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
def human_state
......@@ -48,7 +51,8 @@ class MergeRequest < ActiveRecord::Base
end
def mark_as_unchecked
self.update_attributes(state: UNCHECKED)
self.state = UNCHECKED
self.save
end
def can_be_merged?
......@@ -131,7 +135,8 @@ class MergeRequest < ActiveRecord::Base
end
def mark_as_unmergable
self.update_attributes state: CANNOT_BE_MERGED
self.state = CANNOT_BE_MERGED
self.save
end
def reloaded_commits
......@@ -162,7 +167,7 @@ class MergeRequest < ActiveRecord::Base
end
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)
true
end
......@@ -182,24 +187,30 @@ class MergeRequest < ActiveRecord::Base
patch_path
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
# == Schema Information
#
# Table name: merge_requests
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# project_id :integer(4) not null
# author_id :integer(4)
# assignee_id :integer(4)
# project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# closed :boolean(1) default(FALSE), not null
# closed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# st_commits :text(2147483647
# st_diffs :text(2147483647
# merged :boolean(1) default(FALSE), not null
# state :integer(4) default(1), not null
# st_commits :text(4294967295
# st_diffs :text(4294967295
# merged :boolean default(FALSE), not null
# state :integer default(1), not null
#
# == Schema Information
#
# Table name: milestones
#
# id :integer(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
attr_accessible :title, :description, :due_date, :closed
belongs_to :project
has_many :issues
validates_presence_of :project_id
validates_presence_of :title
validates :title, presence: true
validates :project, presence: true
def self.active
where("due_date > ? OR due_date IS NULL", Date.today)
end
def participants
User.where(id: issues.map(&:assignee_id))
User.where(id: issues.pluck(:assignee_id))
end
def percent_complete
......@@ -37,3 +25,18 @@ class Milestone < ActiveRecord::Base
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
end
end
# == Schema Information
#
# Table name: milestones
#
# id :integer not null, primary key
# title :string(255) not null
# project_id :integer not null
# description :text
# due_date :date
# closed :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
#
......@@ -2,52 +2,42 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Note < ActiveRecord::Base
belongs_to :project
belongs_to :noteable, polymorphic: true
belongs_to :author,
class_name: "User"
delegate :name,
to: :project,
prefix: true
delegate :name,
:email,
to: :author,
prefix: true
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code
attr_protected :author, :author_id
attr_accessor :notify
attr_accessor :notify_author
validates_presence_of :project
belongs_to :project
belongs_to :noteable, polymorphic: true
belongs_to :author, class_name: "User"
validates :note,
presence: true,
length: { within: 0..5000 }
delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true
validates :attachment,
file_size: {
maximum: 10.megabytes.to_i
}
validates :project, presence: true
validates :note, presence: true, length: { within: 0..5000 }
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 :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 :inc_author_project, includes(:project, :author)
scope :inc_author, includes(:author)
mount_uploader :attachment, AttachmentUploader
def self.create_status_change_note(noteable, author, status)
create({ noteable: noteable,
project: noteable.project,
author: author,
note: "_Status changed to #{status}_" },
without_protection: true)
create({
noteable: noteable,
project: noteable.project,
author: author,
note: "_Status changed to #{status}_"
}, without_protection: true)
end
def notify
......@@ -58,11 +48,12 @@ class Note < ActiveRecord::Base
@notify_author ||= false
end
def target
if noteable_type == "Commit"
# override to return commits, which are not active record
def noteable
if for_commit?
project.commit(noteable_id)
else
noteable
super
end
# Temp fix to prevent app crash
# if note commit id doesnt exist
......@@ -84,18 +75,22 @@ class Note < ActiveRecord::Base
# Boolean
#
def notify_only_author?(user)
commit? && commit_author &&
for_commit? && commit_author &&
commit_author.email != user.email
end
def commit?
def for_commit?
noteable_type == "Commit"
end
def for_diff_line?
line_code.present?
end
def commit_author
@commit_author ||=
project.users.find_by_email(target.author_email) ||
project.users.find_by_name(target.author_name)
project.users.find_by_email(noteable.author_email) ||
project.users.find_by_name(noteable.author_name)
rescue
nil
end
......@@ -112,18 +107,19 @@ class Note < ActiveRecord::Base
note.start_with?('-1') || note.start_with?(':-1:')
end
end
# == Schema Information
#
# Table name: notes
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# note :text
# noteable_id :string(255)
# noteable_type :string(255)
# author_id :integer(4)
# author_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# project_id :integer(4)
# project_id :integer
# attachment :string(255)
# line_code :string(255)
#
......
......@@ -6,9 +6,12 @@ class Project < ActiveRecord::Base
include Authority
include Team
#
attr_accessible :name, :path, :description, :code, :default_branch, :issues_enabled,
:wall_enabled, :merge_requests_enabled, :wiki_enabled
attr_accessor :error_code
# Relations
#
belongs_to :group
belongs_to :owner, class_name: "User"
has_many :users, through: :users_projects
has_many :events, dependent: :destroy
......@@ -22,52 +25,66 @@ class Project < ActiveRecord::Base
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
has_many :wikis, dependent: :destroy
has_many :protected_branches, dependent: :destroy
has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
attr_accessor :error_code
delegate :name, to: :owner, allow_nil: true, prefix: true
#
# Protected attributes
#
attr_protected :private_flag, :owner_id
# Validations
validates :owner, presence: true
validates :description, length: { within: 0..2000 }
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
#
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
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
end
def self.search query
where("name like :query or code like :query or path like :query", query: "%#{query}%")
end
def self.create_by_user(params, user)
project = Project.new params
Project.transaction do
project.owner = user
project.save!
class << self
def active
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
end
# Add user as project master
project.users_projects.create!(project_access: UsersProject::MASTER, user: user)
def search query
where("name LIKE :query OR code LIKE :query OR path LIKE :query", query: "%#{query}%")
end
# when project saved no team member exist so
# project repository should be updated after first user add
project.update_repository
def create_by_user(params, user)
project = Project.new params
Project.transaction do
project.owner = user
project.save!
# Add user as project master
project.users_projects.create!(project_access: UsersProject::MASTER, user: user)
# when project saved no team member exist so
# project repository should be updated after first user add
project.update_repository
end
project
rescue Gitlab::Gitolite::AccessDenied => ex
project.error_code = :gitolite
project
rescue => ex
project.error_code = :db
project.errors.add(:base, "Can't save project. Please try again later")
project
end
project
rescue Gitlab::Gitolite::AccessDenied => ex
project.error_code = :gitolite
project
rescue => ex
project.error_code = :db
project.errors.add(:base, "Can't save project. Please try again later")
project
def access_options
UsersProject.access_roles
end
end
def git_error?
......@@ -78,37 +95,6 @@ class Project < ActiveRecord::Base
id && valid?
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
unless owner.can_create_project?
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
......@@ -123,10 +109,6 @@ class Project < ActiveRecord::Base
end
end
def self.access_options
UsersProject.access_roles
end
def to_param
code
end
......@@ -148,7 +130,7 @@ class Project < ActiveRecord::Base
end
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
def public?
......@@ -160,43 +142,44 @@ class Project < ActiveRecord::Base
end
def last_activity
events.order("created_at ASC").last
last_event
end
def last_activity_date
if events.last
events.last.created_at
else
updated_at
end
last_event.try(:created_at) || updated_at
end
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
def project_id
self.id
end
def issues_labels
issues.tag_counts_on(:labels)
end
end
# == Schema Information
#
# Table name: projects
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
# created_at :datetime not null
# updated_at :datetime not null
# private_flag :boolean(1) default(TRUE), not null
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer(4)
# owner_id :integer
# default_branch :string(255)
# issues_enabled :boolean(1) default(TRUE), not null
# wall_enabled :boolean(1) default(TRUE), not null
# merge_requests_enabled :boolean(1) default(TRUE), not null
# wiki_enabled :boolean(1) default(TRUE), not null
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
# group_id :integer
#
class ProjectHook < WebHook
belongs_to :project
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
include GitHost
attr_accessible :name
belongs_to :project
validates_presence_of :project_id
validates_presence_of :name
validates :name, presence: true
validates :project, presence: true
after_save :update_repository
after_destroy :update_repository
......@@ -16,12 +18,13 @@ class ProtectedBranch < ActiveRecord::Base
project.commit(self.name)
end
end
# == Schema Information
#
# Table name: protected_branches
#
# id :integer(4) not null, primary key
# project_id :integer(4) not null
# id :integer not null, primary key
# project_id :integer not null
# name :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
......
class Snippet < ActiveRecord::Base
include Linguist::BlobHelper
attr_accessible :title, :content, :file_name, :expires_at
belongs_to :project
belongs_to :author, class_name: "User"
has_many :notes, as: :noteable, dependent: :destroy
delegate :name,
: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 }
delegate :name, :email, to: :author, prefix: true
validates :file_name,
presence: true,
length: { within: 0..255 }
validates :content,
presence: true,
length: { within: 0..10000 }
validates :author, presence: true
validates :project, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :file_name, presence: true, length: { within: 0..255 }
validates :content, presence: true, length: { within: 0..10000 }
# Scopes
scope :fresh, order("created_at DESC")
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])
......@@ -46,11 +36,11 @@ class Snippet < ActiveRecord::Base
0
end
def name
def name
file_name
end
def mode
def mode
nil
end
......@@ -58,15 +48,16 @@ class Snippet < ActiveRecord::Base
expires_at && expires_at < Time.current
end
end
# == Schema Information
#
# Table name: snippets
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# title :string(255)
# content :text
# author_id :integer(4) not null
# project_id :integer(4) not null
# author_id :integer not null
# project_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# file_name :string(255)
......
class SystemHook < WebHook
def async_execute(data)
Resque.enqueue(SystemHookWorker, id, data)
end
def self.all_hooks_fire(data)
SystemHook.all.each do |sh|
sh.async_execute data
end
end
def async_execute(data)
Resque.enqueue(SystemHookWorker, id, data)
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 Tree
include Linguist::BlobHelper
include Linguist::BlobHelper
attr_accessor :path, :tree, :project, :ref
delegate :contents,
:basename,
:name,
:data,
:mime_type,
:mode,
:size,
:text?,
:colorize,
to: :tree
delegate :contents, :basename, :name, :data, :mime_type,
:mode, :size, :text?, :colorize, to: :tree
def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path,
@tree = if path
@project, @ref, @path = project, ref, path
@tree = if path.present?
raw_tree / path.dup.force_encoding('ascii-8bit')
else
raw_tree
......@@ -26,6 +18,10 @@ class Tree
tree.is_a?(Grit::Blob)
end
def invalid?
tree.nil?
end
def empty?
data.blank?
end
......
class User < ActiveRecord::Base
include Account
devise :database_authenticatable, :token_authenticatable, :lockable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password, :extern_uid, :provider
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name,
:skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password,
:extern_uid, :provider, :as => [:default, :admin]
attr_accessible :projects_limit, :as => :admin
attr_accessor :force_random_password
has_many :users_projects, dependent: :destroy
has_many :keys, dependent: :destroy
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 :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 :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}
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 :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
validates :bio, length: { within: 0..255 }
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
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 :blocked, where(blocked: true)
scope :active, where(blocked: false)
before_validation :generate_password, on: :create
def generate_password
if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8)
class << self
def filter filter_name
case filter_name
when "admins"; self.admins
when "blocked"; self.blocked
when "wop"; self.without_projects
else
self.active
end
end
end
def self.filter filter_name
case filter_name
when "admins"; self.admins
when "blocked"; self.blocked
when "wop"; self.without_projects
else
self.active
def without_projects
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end
end
def self.without_projects
where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)')
end
def create_from_omniauth(auth, ldap = false)
gitlab_auth.create_from_omniauth(auth, ldap)
end
def self.create_from_omniauth(auth, ldap = false)
gitlab_auth.create_from_omniauth(auth, ldap)
end
def find_or_new_for_omniauth(auth)
gitlab_auth.find_or_new_for_omniauth(auth)
end
def self.find_or_new_for_omniauth(auth)
gitlab_auth.find_or_new_for_omniauth(auth)
end
def find_for_ldap_auth(auth, signed_in_resource = nil)
gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
end
def self.find_for_ldap_auth(auth, signed_in_resource = nil)
gitlab_auth.find_for_ldap_auth(auth, signed_in_resource)
end
def gitlab_auth
Gitlab::Auth.new
end
def self.gitlab_auth
Gitlab::Auth.new
def search query
where("name LIKE :query or email LIKE :query", query: "%#{query}%")
end
end
def self.search query
where("name like :query or email like :query", query: "%#{query}%")
def generate_password
if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8)
end
end
end
# == Schema Information
#
# Table name: users
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(128) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer(4) default(0)
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
......@@ -124,14 +98,19 @@ end
# created_at :datetime not null
# updated_at :datetime not null
# name :string(255)
# admin :boolean(1) default(FALSE), not null
# projects_limit :integer(4) default(10)
# admin :boolean default(FALSE), not null
# projects_limit :integer default(10)
# skype :string(255) default(""), not null
# linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null
# authentication_token :string(255)
# dark_scheme :boolean(1) default(FALSE), not null
# theme_id :integer(4) default(1), not null
# dark_scheme :boolean default(FALSE), not null
# theme_id :integer default(1), not null
# 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,70 +6,72 @@ class UsersProject < ActiveRecord::Base
DEVELOPER = 30
MASTER = 40
attr_accessible :user, :user_id, :project_access
belongs_to :user
belongs_to :project
attr_protected :project_id, :project
after_save :update_repository
after_destroy :update_repository
validates_uniqueness_of :user_id, scope: [:project_id], message: "already exists in project"
validates_presence_of :user_id
validates_presence_of :project_id
validates :user, presence: true
validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" }
validates :project, presence: true
delegate :name, :email, to: :user, prefix: true
def self.bulk_delete(project, user_ids)
UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.destroy
class << self
def bulk_delete(project, user_ids)
UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.destroy
end
end
end
end
def self.bulk_update(project, user_ids, project_access)
UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.project_access = project_access
users_project.save
def bulk_update(project, user_ids, project_access)
UsersProject.transaction do
UsersProject.where(:user_id => user_ids, :project_id => project.id).each do |users_project|
users_project.project_access = project_access
users_project.save
end
end
end
end
def self.bulk_import(project, user_ids, project_access)
UsersProject.transaction do
user_ids.each do |user_id|
users_project = UsersProject.new(
project_access: project_access,
user_id: user_id
)
users_project.project = project
users_project.save
def bulk_import(project, user_ids, project_access)
UsersProject.transaction do
user_ids.each do |user_id|
users_project = UsersProject.new(
project_access: project_access,
user_id: user_id
)
users_project.project = project
users_project.save
end
end
end
end
def self.user_bulk_import(user, project_ids, project_access)
UsersProject.transaction do
project_ids.each do |project_id|
users_project = UsersProject.new(
project_access: project_access,
)
users_project.project_id = project_id
users_project.user_id = user.id
users_project.save
def user_bulk_import(user, project_ids, project_access)
UsersProject.transaction do
project_ids.each do |project_id|
users_project = UsersProject.new(
project_access: project_access,
)
users_project.project_id = project_id
users_project.user_id = user.id
users_project.save
end
end
end
end
def self.access_roles
{
"Guest" => GUEST,
"Reporter" => REPORTER,
"Developer" => DEVELOPER,
"Master" => MASTER
}
def access_roles
{
"Guest" => GUEST,
"Reporter" => REPORTER,
"Developer" => DEVELOPER,
"Master" => MASTER
}
end
end
def role_access
......@@ -88,15 +90,16 @@ class UsersProject < ActiveRecord::Base
self.class.access_roles.invert[self.project_access]
end
end
# == Schema Information
#
# Table name: users_projects
#
# id :integer(4) not null, primary key
# user_id :integer(4) not null
# project_id :integer(4) not null
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_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
include HTTParty
attr_accessible :url
# HTTParty timeout
default_timeout 10
validates :url,
presence: true,
format: {
with: URI::regexp(%w(http https)),
message: "should be a valid url" }
validates :url, presence: true,
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }
def execute(data)
parsed_url = URI.parse(url)
if parsed_url.userinfo.blank?
WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
else
post_url = url.gsub(parsed_url.userinfo+"@", "")
post_url = url.gsub("#{parsed_url.userinfo}@", "")
WebHook.post(post_url,
body: data.to_json,
headers: { "Content-Type" => "application/json" },
headers: {"Content-Type" => "application/json"},
basic_auth: {username: parsed_url.user, password: parsed_url.password})
end
end
end
# == Schema Information
#
# Table name: web_hooks
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# url :string(255)
# project_id :integer(4)
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255) default("ProjectHook")
#
class Wiki < ActiveRecord::Base
attr_accessible :title, :content, :slug
belongs_to :project
belongs_to :user
has_many :notes, as: :noteable, dependent: :destroy
validates :content, :title, :user_id, presence: true
validates :title, length: 1..250
validates :content, presence: true
validates :user, presence: true
validates :title, presence: true, length: 1..250
before_update :set_slug
......@@ -14,33 +17,33 @@ class Wiki < ActiveRecord::Base
protected
def self.regenerate_from wiki
regenerated_field = [:slug, :content, :title]
new_wiki = Wiki.new
regenerated_field.each do |field|
new_wiki.send("#{field}=", wiki.send(field))
end
new_wiki
end
def set_slug
self.slug = self.title.parameterize
end
class << self
def regenerate_from wiki
regenerated_field = [:slug, :content, :title]
new_wiki = Wiki.new
regenerated_field.each do |field|
new_wiki.send("#{field}=", wiki.send(field))
end
new_wiki
end
end
end
# == Schema Information
#
# Table name: wikis
#
# id :integer(4) not null, primary key
# id :integer not null, primary key
# title :string(255)
# content :text
# project_id :integer(4)
# project_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# 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
def after_create(users_project)
def after_commit(users_project)
return if users_project.destroyed?
Notify.project_access_granted_email(users_project.id).deliver
end
def after_create(users_project)
Event.create(
project_id: users_project.project.id,
action: Event::Joined,
......@@ -9,10 +12,6 @@ class UsersProjectObserver < ActiveRecord::Observer
)
end
def after_update(users_project)
Notify.project_access_granted_email(users_project.id).deliver
end
def after_destroy(users_project)
Event.create(
project_id: users_project.project.id,
......
......@@ -22,6 +22,10 @@ module Account
projects_limit > my_own_projects.count
end
def can_create_group?
is_admin?
end
def last_activity_project
projects.first
end
......@@ -41,7 +45,7 @@ module Account
# Remove user from all projects and
# set blocked attribute to true
def block
users_projects.all.each do |membership|
users_projects.find_each do |membership|
return false unless membership.destroy
end
......
......@@ -2,12 +2,12 @@ module Authority
# Compatible with all access rights
# Should be rewrited for new access rights
def add_access(user, *access)
access = if access.include?(:admin)
{ project_access: UsersProject::MASTER }
access = if access.include?(:admin)
{ project_access: UsersProject::MASTER }
elsif access.include?(:write)
{ project_access: UsersProject::DEVELOPER }
{ project_access: UsersProject::DEVELOPER }
else
{ project_access: UsersProject::REPORTER }
{ project_access: UsersProject::REPORTER }
end
opts = { user: user }
opts.merge!(access)
......@@ -53,6 +53,6 @@ module Authority
end
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
......@@ -3,24 +3,21 @@ module IssueCommonality
extend ActiveSupport::Concern
included do
attr_protected :author, :author_id, :project, :project_id
belongs_to :project
belongs_to :author, class_name: "User"
belongs_to :assignee, class_name: "User"
has_many :notes, as: :noteable, dependent: :destroy
validates_presence_of :project_id
validates_presence_of :author_id
validates :title,
presence: true,
length: { within: 0..255 }
validates :project, presence: true
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] }
scope :opened, where(closed: false)
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,
:email,
......@@ -49,4 +46,21 @@ module IssueCommonality
def new?
today? && created_at == updated_at
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
......@@ -5,11 +5,11 @@ module PushEvent
false
end
def tag?
def tag?
data[:ref]["refs/tags"]
end
def branch?
def branch?
data[:ref]["refs/heads"]
end
......@@ -25,7 +25,7 @@ module PushEvent
commit_to =~ /^00000/
end
def md_ref?
def md_ref?
!(rm_ref? || new_ref?)
end
......@@ -37,7 +37,7 @@ module PushEvent
data[:after]
end
def ref_name
def ref_name
if tag?
tag_name
else
......@@ -70,7 +70,7 @@ module PushEvent
if new_ref?
"pushed new"
elsif rm_ref?
"removed #{ref_type}"
"deleted"
else
"pushed to"
end
......
......@@ -32,6 +32,10 @@ module Repository
Commit.commits(repo, ref, path, limit, offset)
end
def last_commit_for(ref, path = nil)
commits(ref, path, 1).first
end
def commits_between(from, to)
Commit.commits_between(repo, from, to)
end
......@@ -45,8 +49,29 @@ module Repository
File.exists?(hook_file)
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
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
def repo
......@@ -79,14 +104,6 @@ module Repository
@heads ||= repo.heads
end
def branches_names
heads.map(&:name)
end
def ref_names
[branches_names + tags].flatten
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
......@@ -109,14 +126,12 @@ module Repository
# - If two or more branches are present, returns the one that has a name
# matching root_ref (default_branch or 'master' if default_branch is nil)
def discover_default_branch
branches = heads.collect(&:name)
if branches.length == 0
if branch_names.length == 0
nil
elsif branches.length == 1
branches.first
elsif branch_names.length == 1
branch_names.first
else
branches.select { |v| v == root_ref }.first
branch_names.select { |v| v == root_ref }.first
end
end
......@@ -144,7 +159,7 @@ module Repository
# Build file path
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)
# Put files into a directory before archiving
......@@ -166,4 +181,9 @@ module Repository
def http_url_to_repo
http_url = [Gitlab.config.url, "/", path, ".git"].join('')
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
......@@ -25,6 +25,10 @@ module StaticModel
id
end
def new_record?
false
end
def persisted?
false
end
......@@ -32,4 +36,12 @@ module StaticModel
def destroyed?
false
end
def ==(other)
if other.is_a? StaticModel
id == other.id
else
super
end
end
end
module Team
def team_member_by_name_or_email(email = nil, name = nil)
user = users.where("email like ? or name like ?", email, name).first
users_projects.find_by_user_id(user.id) if user
def team_member_by_name_or_email(name = nil, email = nil)
user = users.where("name like ? or email like ?", name, email).first
users_projects.where(user: user) if user
end
# 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
Projects
= link_to 'New Project', new_admin_project_path, class: "btn small right"
......@@ -11,7 +12,6 @@
%th Name
%th Path
%th Team Members
%th Post Receive
%th Last Commit
%th Edit
%th.cred Danger Zone!
......@@ -21,7 +21,6 @@
%td= link_to project.name, [:admin, project]
%td= project.path
%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= 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"
......
%h3
= @admin_project.name
= link_to 'Edit', edit_admin_project_path(@admin_project), class: "btn right small"
= render 'admin/shared/projects_head'
%h3.page_title
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
%table.zebra-striped
......@@ -56,7 +68,7 @@
%tr
%td
= 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 '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
= @admin_user.name
%h3.page_title
User: #{@admin_user.name}
- if @admin_user.blocked
%small Blocked
- if @admin_user.admin
%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
......@@ -94,6 +96,6 @@
- project = tm.project
%tr
%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 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
%ul.nav.nav-tabs
%li
= render partial: 'shared/ref_switcher', locals: {destination: 'tree', path: params[:path]}
%li{class: "#{'active' if (controller.controller_name == "refs") }"}
= link_to tree_project_ref_path(@project, @ref) do
Source
= nav_link(controller: :refs) do
= link_to 'Source', project_tree_path(@project, @ref)
%li.right
.input-prepend.project_clone_holder
%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"
= render "head"
#tree-holder
#tree-holder.tree-holder
%ul.breadcrumb
%li
%span.arrow
= link_to tree_project_ref_path(@project, @ref, path: nil) do
= link_to project_tree_path(@project, @ref) do
= @project.name
- @tree.breadcrumbs(6) do |link|
\/
......@@ -15,12 +15,9 @@
.file_title
%i.icon-file
%span.file_name
= @tree.name
%small blame
%span.options
= 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"
= @tree.name.force_encoding('utf-8')
%small= number_to_human_size @tree.size
%span.options= render "tree/blob_actions"
.file_content.blame
%table
- @blame.each do |commit, lines|
......@@ -32,8 +29,8 @@
= commit.author_name
%td.blame_commit
&nbsp;
%code= link_to commit.short_id, project_commit_path(@project, id: commit.id)
= link_to_gfm truncate(commit.title, length: 30), project_commit_path(@project, id: commit.id), class: "row_title" rescue "--broken encoding"
%code= link_to commit.short_id, project_commit_path(@project, commit)
= link_to_gfm truncate(commit.title, length: 30), project_commit_path(@project, commit), class: "row_title" rescue "--broken encoding"
%td.lines
= preserve do
%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
.browse_code_link_holder
%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
= 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
%span.dash &ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, id: commit.id), class: "row_title"
= link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title"
%span.committed_ago
= time_ago_in_words(commit.committed_date)
ago
&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 @@
%span.btn.disabled.grouped
%i.icon-comment
= @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
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 »
%h3.commit-title.page_title
= gfm escape_once(@commit.title)
......@@ -17,7 +17,7 @@
= gfm escape_once(@commit.description)
.commit-info
.row
.span4
.span5
= image_tag gravatar_icon(@commit.author_email, 40), class: "avatar"
.author
%strong= @commit.author_name
......@@ -31,10 +31,10 @@
committed
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.committed_date)} ago
.span7.right
.span6.right
.sha-block
%span.cgray commit
%code= @commit.id
%code.label_commit= @commit.id
.sha-block
%span.cgray= pluralize(@commit.parents.count, "parent")
- @commit.parents.each do |parent|
......
......@@ -5,7 +5,7 @@
%p To prevent performance issue we rejected diff information.
%p
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
Showing #{pluralize(diffs.count, "changed file")}
......@@ -24,7 +24,7 @@
%i.icon-file
%span{id: "#{diff.old_path}"}= diff.old_path
- 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
%span{id: "#{diff.new_path}"}= diff.new_path
%br/
......
%ul.nav.nav-tabs
%li= render partial: 'shared/ref_switcher', locals: {destination: 'commits'}
%li{class: "#{'active' if current_page?(project_commits_path(@project)) }"}
= link_to project_commits_path(@project) do
Commits
%li{class: "#{'active' if current_page?(compare_project_commits_path(@project)) }"}
= link_to compare_project_commits_path(@project) do
Compare
%li{class: "#{branches_tab_class}"}
= nav_link(controller: [:commit, :commits]) do
= link_to 'Commits', project_commits_path(@project, @project.root_ref)
= nav_link(controller: :compare) do
= link_to 'Compare', project_compare_index_path(@project)
= nav_link(html_options: {class: branches_tab_class}) do
= link_to project_repository_path(@project) do
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
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
%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"
......@@ -4,7 +4,7 @@
%table{class: "#{'hide' if too_big}"}
- 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"
%td.old_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.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.link :href => project_commits_url(@project, :atom, :ref => @ref), :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, format: :atom), :rel => "self", :type => "application/atom+xml"
xml.link :href => project_commits_url(@project, @ref), :rel => "alternate", :type => "text/html"
xml.id project_commits_url(@project)
xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any?
......
= 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"
= render "head"
- 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(" / ")
%div{id: dom_id(@project)}
#commits_list= render "commits"
.clear
.loading{ style: "display:none;"}
- if @commits.count == @limit
:javascript
$(function(){
CommitsList.init("#{@ref}", #{@limit});
});
:javascript
$(function(){
PerLineNotes.init();
});
= render "head"
%h3.page_title
Compare View
%hr
%div
%p.slead
Fill input field with commit id like
......@@ -14,7 +8,7 @@
%br
= form_tag compare_project_commits_path(@project), method: :get do
= form_tag project_compare_index_path(@project), method: :post do
.clearfix
= text_field_tag :from, params[:from], placeholder: "master", class: "xlarge"
= "..."
......@@ -25,29 +19,14 @@
.actions
= 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
$(function() {
var availableTags = #{@project.ref_names.to_json};
$("#from").autocomplete({
source: availableTags,
minLength: 1
});
$("#to").autocomplete({
$("#from, #to").autocomplete({
source: availableTags,
minLength: 1
});
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
xml.entry do
event_link = event.feed_url
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.link :href => event_link
......@@ -22,7 +23,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.name event.author_name
xml.email event.author_email
end
xml.summary event_title
xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }
end
end
end
......
......@@ -9,28 +9,9 @@
.loading.hide
.side
= render "events/event_last_push", event: @last_push
.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"
- if @groups.present?
= render "groups", groups: @groups
= render "projects", projects: @projects
%div
%span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
......
- commit = CommitDecorator.decorate(commit)
%li.commit
%p
= link_to commit.short_id(8), project_commit_path(project, id: commit.id), class: "commit_short_id"
%strong.cdark= commit.author_name
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
%span= commit.author_name
&ndash;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= gfm escape_once(truncate(commit.title, length: 50)) rescue "--broken encoding"
- if event.allowed?
- if event.issue?
.event_feed
= render "events/event_issue", event: event
- elsif event.merge_request?
.event_feed
= render "events/event_merge_request", event: event
- elsif event.push?
.event_feed
= render "events/event_push", event: event
- elsif event.membership_changed?
.event_feed
= render "events/event_membership_changed", event: event
%div.event-item
= event_image(event)
= image_tag gravatar_icon(event.author_email), class: "avatar"
- if event.push?
= render "events/event/push", event: event
- else
= render "events/event/common", event: event
.clearfix
%span.cgray.right
= time_ago_in_words(event.created_at)
ago.
.clearfix
%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 @@
= image_tag gravatar_icon(event.author_email), class: "avatar"
%span You pushed to
= 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)
at
%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