Merge branch 'master' into api

This commit is contained in:
Nihad Abbasov 2012-07-24 05:46:36 -07:00
commit eca823c1c7
124 changed files with 1743 additions and 657 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ log/*.log
tmp/
.sass-cache/
coverage/*
backups/*
*.swp
public/uploads/
.rvmrc

View file

@ -1,5 +1,13 @@
v 2.7.0
- Issue Labels
- Inline diff
- Git HTTP
- API
- UI improved
- System hooks
- UI improved
- Dashboard events endless scroll
- Source perfomance increased
v 2.6.0
- UI polished

View file

@ -7,7 +7,7 @@ gem "sqlite3"
gem "mysql2"
# Auth
gem "devise", "~> 1.5"
gem "devise", "~> 2.1.0"
# GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
@ -71,7 +71,6 @@ group :development, :test do
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
gem "webmock"
end
group :test do
@ -82,4 +81,5 @@ group :test do
gem "shoulda-matchers"
gem 'email_spec'
gem 'resque_spec'
gem "webmock"
end

View file

@ -148,10 +148,11 @@ GEM
nokogiri (>= 1.5.0)
daemons (1.1.8)
database_cleaner (0.8.0)
devise (1.5.3)
devise (2.1.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.0.3)
warden (~> 1.1)
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
diff-lcs (1.1.3)
drapper (0.8.4)
email_spec (1.2.1)
@ -225,7 +226,7 @@ GEM
omniauth (1.1.0)
hashie (~> 1.2)
rack
orm_adapter (0.0.7)
orm_adapter (0.3.0)
polyglot (0.3.3)
posix-spawn (0.3.6)
pry (0.9.9.6)
@ -356,7 +357,7 @@ GEM
raindrops (~> 0.7)
vegas (0.1.11)
rack (>= 1.0.0)
warden (1.2.0)
warden (1.2.1)
rack (>= 1.0)
webmock (1.8.7)
addressable (>= 2.2.7)
@ -383,7 +384,7 @@ DEPENDENCIES
colored
cucumber-rails
database_cleaner
devise (~> 1.5)
devise (~> 2.1.0)
drapper
email_spec
ffaker

View file

@ -1 +1 @@
2.7.0pre
2.7.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -12,6 +12,7 @@
//= require jquery.cookie
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.waitforimages
//= require bootstrap-modal
//= require modernizr
//= require chosen-jquery
@ -20,10 +21,26 @@
//= require_tree .
$(document).ready(function(){
$(".one_click_select").live("click", function(){
$(this).select();
});
$('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
var buttons = $('[type="submit"]', this);
switch( e.type ){
case 'ajax:beforeSend':
case 'submit':
buttons.attr('disabled', 'disabled');
break;
case ' ajax:complete':
default:
buttons.removeAttr('disabled');
break;
}
})
$(".account-box").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu);
@ -97,3 +114,8 @@ function showDiff(link) {
return _chosen.apply(this, [default_options]);
}})
})(jQuery);
function ajaxGet(url) {
$.ajax({type: "GET", url: url, dataType: "script"});
}

View file

@ -73,4 +73,25 @@ function issuesPage(){
$("#milestone_id, #assignee_id, #label_name").on("change", function(){
$(this).closest("form").submit();
});
$('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
var t = $(this),
totalIssues,
reopen = t.hasClass('reopen_issue'),
newIssue = false;
if( this.id == 'new_issue' ){
newIssue = true;
}
$('.issue_counter, #new_issue').each(function(){
var issue = $(this);
totalIssues = parseInt( $(this).html(), 10 );
if( newIssue || ( reopen && issue.closest('.main_menu').length ) ){
$(this).html( totalIssues+1 );
}else {
$(this).html( totalIssues-1 );
}
});
});
}

View file

@ -25,11 +25,11 @@ init:
$(this).closest('li').fadeOut(); });
$("#new_note").live("ajax:before", function(){
$("#submit_note").attr("disabled", "disabled");
$(".submit_note").attr("disabled", "disabled");
})
$("#new_note").live("ajax:complete", function(){
$("#submit_note").removeAttr("disabled");
$(".submit_note").removeAttr("disabled");
})
$("#note_note").live("focus", function(){

View file

@ -604,7 +604,11 @@ li.note {
border-style: solid;
border-width: 1px;
@include border-radius(4px);
min-height:42px;
min-height:22px;
.avatar {
width:24px;
}
}
.supp_diff_link,

View file

@ -202,6 +202,10 @@ a:focus {
color:$style_color;
}
.nav-tabs > .active > a {
font-weight:bold;
}
/** COLORS **/
.cgray { color:gray; }
.cred { color:#D12F19; }
@ -209,6 +213,7 @@ a:focus {
.cblack { color:#111; }
.cdark { color:#444 }
.cwhite { color:#fff !important }
.bgred { background: #F2DEDE !important}
/** COMMON STYLES **/
.left {
@ -299,9 +304,24 @@ table.no-borders {
}
.event_label {
background: #FCEEC1;
padding: 2px 2px 0;
font-family: monospace;
@extend .label;
background-color: #999;
&.pushed {
background-color: #3A87AD;
}
&.opened {
background-color: #468847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
}
img.avatar {
@ -425,9 +445,10 @@ form {
*/
.ui-box {
background:#F9F9F9;
margin-bottom: 40px;
margin-bottom: 25px;
@include round-borders-all(4px);
border-color: #CCC;
@include solid_shade;
ul {
margin:0;
@ -443,6 +464,13 @@ form {
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
&.small {
line-height: 28px;
font-size: 14px;
line-height:28px;
text-shadow: 0 1px 1px white;
}
form {
padding:9px 0;
margin:0px;
@ -511,6 +539,7 @@ form {
table.admin-table {
@extend .table-bordered;
@extend .zebra-striped;
@include solid_shade;
th {
border-color: #CCC;
border-bottom: 1px solid #bbb;
@ -568,6 +597,8 @@ ul.breadcrumb {
@extend .prepend-top-20;
@extend .append-bottom-20;
border-width:1px;
@include solid_shade;
img { max-width: 100%; }
@ -624,13 +655,166 @@ p {
h3.page_title {
color:#456;
font-size:20px;
font-weight: 600;
font-weight: normal;
line-height: 28px;
}
pre.logs {
.log {
font-size:12px;
line-height:18px;
/**
* File content holder
*
*/
.file_holder {
border:1px solid #CCC;
margin-bottom:1em;
@include solid_shade;
.file_title {
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
padding: 9px 10px;
height:18px;
.options {
float:right;
margin-top: -5px;
}
.file_name {
color:$style_color;
font-size:14px;
text-shadow: 0 1px 1px #fff;
small {
color:#999;
font-size:13px;
}
}
}
.file_content {
background:#fff;
font-size: 11px;
&.wiki {
font-size: 13px;
code {
padding:0 4px;
}
padding:20px;
h1, h2 {
line-height: 46px;
}
h3, h4 {
line-height: 40px;
}
}
&.image_file {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
&.blob_file {
}
/**
* Blame file
*/
&.blame {
tr {
border-bottom: 1px solid #eee;
}
td {
padding:5px;
}
.author,
.blame_commit {
background:#f5f5f5;
vertical-align:top;
}
.lines {
pre {
padding:0;
margin:0;
background:none;
border:none;
}
}
}
&.logs {
background:#eee;
max-height: 700px;
overflow-y: auto;
ol {
margin-left:40px;
padding: 10px 0;
border-left: 1px solid #CCC;
margin-bottom:0;
background: white;
li {
color:#888;
p {
margin:0;
color:#333;
line-height:24px;
padding-left: 10px;
}
&:hover {
background:$hover;
}
}
}
}
/**
* Code file
*/
&.code {
padding:0;
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#666;
}
}
}
}

View file

@ -96,7 +96,7 @@ header {
*/
.search {
float: right;
margin-right: 55px;
margin-right: 50px;
.search-input {
@extend .span2;
@ -126,10 +126,10 @@ header {
cursor: pointer;
img {
border-radius: 4px;
right: 0px;
right: 5px;
position: absolute;
width: 33px;
height: 33px;
width: 31px;
height: 31px;
display: block;
top: 0;
&:after {

View file

@ -31,6 +31,12 @@ $hover: #FDF5D9;
box-shadow: 0 0 3px #ddd;
}
@mixin solid_shade {
-moz-box-shadow: 0 0 0 3px #eee;
-webkit-box-shadow: 0 0 0 3px #eee;
box-shadow: 0 0 0 3px #eee;
}
@mixin border-radius($radius) {
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
@ -136,7 +142,7 @@ $hover: #FDF5D9;
/**
* Code (files list) styles. Browsing project files there
*/
@import "tree.scss";
@import "sections/tree.scss";
/**
* This file represent notes(comments) styles

View file

@ -63,18 +63,22 @@ p.notify_controls span{
tr.line_notes_row {
border-bottom:1px solid #DDD;
border-left: 7px solid #2A79A3;
&.reply {
background:#eee;
border-left: 7px solid #2A79A3;
border-top:1px solid #ddd;
td {
padding:7px 10px;
}
a.line_note_reply_link {
@include round-borders-all(4px);
border-color:#aaa;
background: #bbb;
padding: 3px 20px;
padding: 3px 10px;
margin-left:5px;
color: white;
background: #2A79A3;
border-color: #2A79A3;
}
}
ul {
@ -95,6 +99,9 @@ tr.line_notes_row {
td {
border-bottom:1px solid #ddd;
}
.actions {
margin:0;
}
}
td .line_note_link {

View file

@ -101,16 +101,19 @@
margin:50px;
padding:1px;
max-width:400px;
}
&.diff_image_removed {
img {
&.diff_image_removed {
border: 1px solid #C00;
}
&.diff_image_added {
border: 1px solid #0C0;;
}
}
&.diff_image_added {
img {
border: 1px solid #0C0;;
&.img_compared {
img {
max-width:300px;
}
}
}

View file

@ -82,3 +82,15 @@
}
}
}
li.merge_request {
padding:7px 10px;
img.avatar {
width: 32px;
margin-top: 4px;
}
p {
padding: 0px;
padding-bottom: 2px;
}
}

View file

@ -0,0 +1,96 @@
#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;
margin:20px;
&.loading {
display:block;
}
}
#tree-slider {
@include border-radius(0);
.tree-item {
&:hover {
td { background: $hover; }
cursor:pointer;
}
}
}
.tree-item {
.tree-item-file-name {
vertical-align:middle;
font-weight:bold;
a {
color:$style_color;
&:hover {
color:$blue_link;
}
}
img {
position: relative;
top:-1px;
}
}
}
#tree-slider {
@include solid_shade;
width:100%;
border-color:#ccc;
td {
padding:8px;
border-color:#f1f1f1;
background:#fafafa;
}
tr:first-child td:first-child,
tr:first-child td:last-child {
border-radius:0;
}
th {
border-color: #CCC;
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
}
.tree-commit-link {
color:#333;
}
a.tree-commit-link {
color: #666;
&:hover {
text-decoration: underline;
}
}
}

View file

@ -70,8 +70,7 @@
}
}
.separator {
border-color:#444;
background:#31363E;
display:none;
}
}

View file

@ -1,232 +0,0 @@
#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;
margin:20px;
&.loading {
display:block;
}
}
/** FILE CONTENT VIEW **/
.view_file_content{
.old_line, .new_line {
background:#ECECEC;
color:#777;
width:15px;
float:left;
padding: 0px 10px;
border-right: 1px solid #ccc;
}
.old_line{
display:none;
}
}
.view_file .view_file_header,
.diff_file .diff_file_header {
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
padding: 9px 10px;
height:18px;
.options {
float:right;
margin-top: -5px;
}
.file_name {
color:$style_color;
font-size:14px;
text-shadow: 0 1px 1px #fff;
small {
color:#999;
font-size:13px;
}
}
}
.view_file {
border:1px solid #CCC;
margin-bottom:1em;
.view_file_content {
background:#fff;
color:#514721;
font-size: 11px;
}
.view_file_content_image {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
}
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#666;
}
#tree-slider {
@include border-radius(0);
.tree-item {
&:hover {
td { background: $hover; }
cursor:pointer;
}
}
}
.tree-item {
.tree-item-file-name {
vertical-align:middle;
font-weight:bold;
a {
color:$style_color;
&:hover {
color:$blue_link;
}
}
img {
position: relative;
top:-1px;
}
}
}
#tree-slider {
@include shade;
width:100%;
border-color:#ccc;
td {
padding:8px;
border-color:#f1f1f1;
background:#fafafa;
}
tr:first-child td:first-child,
tr:first-child td:last-child {
border-radius:0;
}
th {
border-color: #CCC;
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
}
}
.tree-commit-link {
color:#333;
}
#tree-content-holder .view_file{
@include shade;
}
#tree-readme-holder .readme {
@include shade;
margin-bottom:20px;
h1, h2 {
line-height: 56px;
}
h3, h4 {
line-height: 46px;
}
}
a.tree-commit-link {
color: #666;
&:hover {
text-decoration: underline;
}
}
}
.blame_file {
.view_file_content {
tr {
border-bottom: 1px solid #eee;
}
td {
padding:5px;
}
.author,
.commit {
background:#f5f5f5;
vertical-align:top;
}
.lines {
pre {
padding:0;
margin:0;
background:none;
border:none;
}
}
}
}

View file

@ -0,0 +1,8 @@
class BaseContext
attr_accessor :project, :current_user, :params
def initialize(project, user, params)
@project, @current_user, @params = project, user, params.dup
end
end

View file

@ -0,0 +1,26 @@
class CommitLoad < BaseContext
def execute
result = {
:commit => nil,
:suppress_diff => false,
:line_notes => [],
:notes_count => 0,
:note => nil
}
commit = project.commit(params[:id])
if commit
commit = CommitDecorator.decorate(commit)
line_notes = project.commit_line_notes(commit)
result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff]
result[:commit] = commit
result[:note] = project.build_commit_note(commit)
result[:line_notes] = line_notes
result[:notes_count] = line_notes.count + project.commit_notes(commit).count
end
result
end
end

View file

@ -0,0 +1,16 @@
class MergeRequestsLoad < BaseContext
def execute
type = params[:f].to_i
merge_requests = project.merge_requests
merge_requests = case type
when 1 then merge_requests
when 2 then merge_requests.closed
when 3 then merge_requests.opened.assigned(current_user)
else merge_requests.opened
end.page(params[:page]).per(20)
merge_requests.includes(:author, :project).order("closed, created_at desc")
end
end

View file

@ -0,0 +1,30 @@
class NotesLoad < BaseContext
def execute
target_type = params[:target_type]
target_id = params[:target_id]
first_id = params[:first_id]
last_id = params[:last_id]
@notes = case target_type
when "commit"
then project.commit_notes(project.commit(target_id)).fresh.limit(20)
when "snippet"
then project.snippets.find(target_id).notes
when "wall"
then project.common_notes.order("created_at DESC").fresh.limit(50)
when "issue"
then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
when "merge_request"
then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
end
@notes = if last_id
@notes.where("id > ?", last_id)
elsif first_id
@notes.where("id < ?", first_id)
else
@notes
end
end
end

View file

@ -0,0 +1,44 @@
class Admin::HooksController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def index
@hooks = SystemHook.all
@hook = SystemHook.new
end
def create
@hook = SystemHook.new(params[:hook])
if @hook.save
redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
else
@hooks = SystemHook.all
render :index
end
end
def destroy
@hook = SystemHook.find(params[:id])
@hook.destroy
redirect_to admin_hooks_path
end
def test
@hook = SystemHook.find(params[:hook_id])
data = {
event_name: "project_create",
name: "Ruby",
path: "ruby",
project_id: 1,
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
@hook.execute(data)
redirect_to :back
end
end

View file

@ -1,45 +0,0 @@
class Admin::MailerController < ApplicationController
layout "admin"
before_filter :authenticate_user!
before_filter :authenticate_admin!
def preview
end
def preview_note
@note = Note.first
@user = @note.author
@project = @note.project
case params[:type]
when "Commit" then
@commit = @project.commit
render :file => 'notify/note_commit_email', :layout => 'notify'
when "Issue" then
@issue = Issue.first
render :file => 'notify/note_issue_email', :layout => 'notify'
else
render :file => 'notify/note_wall_email', :layout => 'notify'
end
rescue
render :text => "Preview not available"
end
def preview_user_new
@user = User.first
@password = "DHasJKDHAS!"
render :file => 'notify/new_user_email', :layout => 'notify'
rescue
render :text => "Preview not available"
end
def preview_issue_new
@issue = Issue.first
@user = @issue.assignee
@project = @issue.project
render :file => 'notify/new_issue_email', :layout => 'notify'
rescue
render :text => "Preview not available"
end
end

View file

@ -6,7 +6,7 @@ class Admin::ProjectsController < ApplicationController
def index
@admin_projects = Project.scoped
@admin_projects = @admin_projects.search(params[:name]) if params[:name].present?
@admin_projects = @admin_projects.page(params[:page])
@admin_projects = @admin_projects.page(params[:page]).per(20)
end
def show
@ -72,6 +72,6 @@ class Admin::ProjectsController < ApplicationController
@admin_project = Project.find_by_code(params[:id])
@admin_project.destroy
redirect_to admin_projects_url
redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
end
end

View file

@ -52,7 +52,7 @@ class ApplicationController < ActionController::Base
def layout_by_resource
if devise_controller?
"devise"
"devise_layout"
else
"application"
end

View file

@ -26,43 +26,31 @@ class CommitsController < ApplicationController
end
def show
@commit = project.commit(params[:id])
result = CommitLoad.new(project, current_user, params).execute
git_not_found! and return unless @commit
@commit = result[:commit]
@commit = CommitDecorator.decorate(@commit)
@note = @project.build_commit_note(@commit)
@comments_allowed = true
@line_notes = project.commit_line_notes(@commit)
@notes_count = @line_notes.count + project.commit_notes(@commit).count
if @commit.diffs.size > 200 && !params[:force_show_diff]
@suppress_diff = true
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
rescue Grit::Git::GitTimeout
render "huge_commit"
end
def compare
first = project.commit(params[:to].try(:strip))
last = project.commit(params[:from].try(:strip))
result = Commit.compare(project, params[:from], params[:to])
@diffs = []
@commits = []
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@line_notes = []
if first && last
commits = [first, last].sort_by(&:created_at)
younger = commits.first
older = commits.last
@commits = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
@diffs = project.repo.diff(younger.id, older.id) rescue []
@commit = Commit.new(older)
end
end
def patch

View file

@ -2,15 +2,13 @@ class DashboardController < ApplicationController
respond_to :html
def index
@projects = current_user.projects.includes(:events).order("events.created_at DESC")
@projects = @projects.page(params[:page]).per(40)
@events = Event.where(:project_id => current_user.projects.map(&:id)).recent.limit(20)
@projects = current_user.projects_with_events.page(params[:page]).per(40)
@events = Event.recent_for_user(current_user).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

View file

@ -11,24 +11,24 @@ class HooksController < ApplicationController
respond_to :html
def index
@hooks = @project.web_hooks.all
@hook = WebHook.new
@hooks = @project.hooks.all
@hook = ProjectHook.new
end
def create
@hook = @project.web_hooks.new(params[:hook])
@hook = @project.hooks.new(params[:hook])
@hook.save
if @hook.valid?
redirect_to project_hooks_path(@project)
else
@hooks = @project.web_hooks.all
@hooks = @project.hooks.all
render :index
end
end
def test
@hook = @project.web_hooks.find(params[:id])
@hook = @project.hooks.find(params[:id])
commits = @project.commits(@project.default_branch, nil, 3)
data = @project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}", current_user)
@hook.execute(data)
@ -37,7 +37,7 @@ class HooksController < ApplicationController
end
def destroy
@hook = @project.web_hooks.find(params[:id])
@hook = @project.hooks.find(params[:id])
@hook.destroy
redirect_to project_hooks_path(@project)

View file

@ -24,16 +24,7 @@ class MergeRequestsController < ApplicationController
def index
@merge_requests = @project.merge_requests
@merge_requests = case params[:f].to_i
when 1 then @merge_requests
when 2 then @merge_requests.closed
when 3 then @merge_requests.opened.assigned(current_user)
else @merge_requests.opened
end.page(params[:page]).per(20)
@merge_requests = @merge_requests.includes(:author, :project).order("closed, created_at desc")
@merge_requests = MergeRequestsLoad.new(project, current_user, params).execute
end
def show

View file

@ -40,25 +40,6 @@ class NotesController < ApplicationController
protected
def notes
@notes = case params[:target_type]
when "commit"
then project.commit_notes(project.commit((params[:target_id]))).fresh.limit(20)
when "snippet"
then project.snippets.find(params[:target_id]).notes
when "wall"
then project.common_notes.order("created_at DESC").fresh.limit(50)
when "issue"
then project.issues.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20)
when "merge_request"
then project.merge_requests.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20)
end
@notes = if params[:last_id]
@notes.where("id > ?", params[:last_id])
elsif params[:first_id]
@notes.where("id < ?", params[:first_id])
else
@notes
end
@notes = NotesLoad.new(project, current_user, params).execute
end
end

View file

@ -1,4 +1,17 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Extend the standard message generation to accept our custom exception
def failure_message
exception = env["omniauth.error"]
if exception.class == OmniAuth::Error
error = exception.message
else
error = exception.error_reason if exception.respond_to?(:error_reason)
error ||= exception.error if exception.respond_to?(:error)
error ||= env["omniauth.error.type"].to_s
end
error.to_s.humanize if error
end
def ldap
# We only find ourselves here if the authentication to LDAP was successful.

View file

@ -9,7 +9,7 @@ class RefsController < ApplicationController
before_filter :require_non_empty_project
before_filter :ref
before_filter :define_tree_vars, :only => [:tree, :blob, :blame]
before_filter :define_tree_vars, :only => [:tree, :blob, :blame, :logs_tree]
before_filter :render_full_content
layout "project"
@ -46,6 +46,18 @@ class RefsController < ApplicationController
end
end
def logs_tree
contents = @tree.contents
@logs = contents.map do |content|
file = params[:path] ? File.join(params[:path], content.name) : content.name
last_commit = @project.commits(@commit.id, file, 1).last
{
:file_name => content.name,
:commit => last_commit
}
end
end
def blob
if @tree.is_blob?
if @tree.text?
@ -79,6 +91,15 @@ class RefsController < ApplicationController
@commit = project.commit(@ref)
@tree = Tree.new(@commit.tree, project, @ref, params[:path])
@tree = TreeDecorator.new(@tree)
@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])
else
@history_path = tree_project_ref_path(@project, @ref)
@logs_path = logs_tree_project_ref_path(@project, @ref)
end
rescue
return render_404
end

View file

@ -0,0 +1,25 @@
class EventDecorator < ApplicationDecorator
decorates :event
def feed_title
if self.issue?
"#{self.author_name} #{self.action_name} issue ##{self.target_id}:" + self.issue_title
elsif self.merge_request?
"#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title
elsif self.push?
"#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name
else
""
end
end
def feed_url
if self.issue?
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?
h.project_commits_url(self.project, :ref => self.ref_name)
end
end
end

View file

@ -134,4 +134,8 @@ module ApplicationHelper
end
active ? "current" : nil
end
def hexdigest(string)
Digest::SHA1.hexdigest string
end
end

View file

@ -0,0 +1,27 @@
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"
else
image_tag "file_bin.png"
end
else
image_tag "file_dir.png"
end
end
def tree_hex_class(content)
"file_#{hexdigest(content.name)}"
end
def tree_full_path(content)
if params[:path]
File.join(params[:path], content.name)
else
content.name
end
end
end

View file

@ -80,6 +80,29 @@ class Commit
def commits_between(repo, from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
def compare(project, from, to)
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
result = {
:commits => [],
:diffs => [],
:commit => nil
}
if first && last
commits = [first, last].sort_by(&:created_at)
younger = commits.first
older = commits.last
result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older)
end
result
end
end
def persisted?

View file

@ -28,6 +28,10 @@ class Event < ActiveRecord::Base
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

View file

@ -22,7 +22,6 @@ class MergeRequest < ActiveRecord::Base
:should_remove_source_branch
validates_presence_of :project_id
validates_presence_of :assignee_id
validates_presence_of :author_id
validates_presence_of :source_branch
validates_presence_of :target_branch
@ -36,6 +35,7 @@ class MergeRequest < ActiveRecord::Base
delegate :name,
:email,
:to => :assignee,
:allow_nil => true,
:prefix => true
validates :title,
@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base
def unmerged_diffs
commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id)
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
end
def last_commit

View file

@ -19,7 +19,7 @@ class Project < ActiveRecord::Base
has_many :notes, :dependent => :destroy
has_many :snippets, :dependent => :destroy
has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
has_many :web_hooks, :dependent => :destroy
has_many :hooks, :dependent => :destroy, :class_name => "ProjectHook"
has_many :wikis, :dependent => :destroy
has_many :protected_branches, :dependent => :destroy
@ -120,7 +120,7 @@ class Project < ActiveRecord::Base
errors.add(:path, " like 'gitolite-admin' is not allowed")
end
end
def self.access_options
UsersProject.access_roles
end

View file

@ -0,0 +1,3 @@
class ProjectHook < WebHook
belongs_to :project
end

13
app/models/system_hook.rb Normal file
View file

@ -0,0 +1,13 @@
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
end

View file

@ -1,11 +1,12 @@
class User < ActiveRecord::Base
include Account
devise :database_authenticatable, :token_authenticatable,
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,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password
attr_accessor :force_random_password
@ -15,6 +16,11 @@ class User < ActiveRecord::Base
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,
@ -80,7 +86,8 @@ class User < ActiveRecord::Base
def self.find_for_ldap_auth(omniauth_info)
name = omniauth_info.name.force_encoding("utf-8")
email = omniauth_info.email.downcase
email = omniauth_info.email.downcase unless omniauth_info.email.nil?
raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil?
if @user = User.find_by_email(email)
@user

View file

@ -68,7 +68,7 @@ class UsersProject < ActiveRecord::Base
end
def repo_access_human
""
self.class.access_roles.invert[self.project_access]
end
end
# == Schema Information

View file

@ -4,8 +4,6 @@ class WebHook < ActiveRecord::Base
# HTTParty timeout
default_timeout 10
belongs_to :project
validates :url,
presence: true,
format: {
@ -14,9 +12,8 @@ class WebHook < ActiveRecord::Base
def execute(data)
WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
rescue
# There was a problem calling this web hook, let's forget about it.
end
end
# == Schema Information
#

View file

@ -43,7 +43,7 @@ class MailerObserver < ActiveRecord::Observer
end
def new_merge_request(merge_request)
if merge_request.assignee != current_user
if merge_request.assignee && merge_request.assignee != current_user
Notify.new_merge_request_email(merge_request.id).deliver
end
end

View file

@ -0,0 +1,67 @@
class SystemHookObserver < ActiveRecord::Observer
observe :user, :project, :users_project
def after_create(model)
if model.kind_of? Project
SystemHook.all_hooks_fire({
event_name: "project_create",
name: model.name,
path: model.path,
project_id: model.id,
owner_name: model.owner.name,
owner_email: model.owner.email,
created_at: model.created_at
})
elsif model.kind_of? User
SystemHook.all_hooks_fire({
event_name: "user_create",
name: model.name,
email: model.email,
created_at: model.created_at
})
elsif model.kind_of? UsersProject
SystemHook.all_hooks_fire({
event_name: "user_add_to_team",
project_name: model.project.name,
project_path: model.project.path,
project_id: model.project_id,
user_name: model.user.name,
user_email: model.user.email,
project_access: model.repo_access_human,
created_at: model.created_at
})
end
end
def after_destroy(model)
if model.kind_of? Project
SystemHook.all_hooks_fire({
event_name: "project_destroy",
name: model.name,
path: model.path,
project_id: model.id,
owner_name: model.owner.name,
owner_email: model.owner.email,
})
elsif model.kind_of? User
SystemHook.all_hooks_fire({
event_name: "user_destroy",
name: model.name,
email: model.email
})
elsif model.kind_of? UsersProject
SystemHook.all_hooks_fire({
event_name: "user_remove_from_team",
project_name: model.project.name,
project_path: model.project.path,
project_id: model.project_id,
user_name: model.user.name,
user_email: model.user.email,
project_access: model.repo_access_human
})
end
end
end

View file

@ -55,4 +55,8 @@ module Account
# Take only latest one
events = events.recent.limit(1).first
end
def projects_with_events
projects.includes(:events).order("events.created_at DESC")
end
end

View file

@ -27,7 +27,7 @@ module GitPush
true
end
def execute_web_hooks(oldrev, newrev, ref, user)
def execute_hooks(oldrev, newrev, ref, user)
ref_parts = ref.split('/')
# Return if this is not a push to a branch (e.g. new commits)
@ -35,7 +35,7 @@ module GitPush
data = post_receive_data(oldrev, newrev, ref, user)
web_hooks.each { |web_hook| web_hook.execute(data) }
hooks.each { |hook| hook.execute(data) }
end
def post_receive_data(oldrev, newrev, ref, user)
@ -97,7 +97,7 @@ module GitPush
self.update_merge_requests(oldrev, newrev, ref, user)
# Execute web hooks
self.execute_web_hooks(oldrev, newrev, ref, user)
self.execute_hooks(oldrev, newrev, ref, user)
# Create satellite
self.satellite.create unless self.satellite.exists?

View file

@ -0,0 +1,66 @@
<% data_ex_str = <<eos
1. Project created:
{
"created_at": "2012-07-21T07:30:54Z",
"event_name": "project_create",
"name": "StoreCloud",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
"path": "storecloud",
"project_id": 74
}
2. Project destroyed:
{
"event_name": "project_destroy",
"name": "Underscore",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
"path": "underscore",
"project_id": 73
}
3. New Team Member:
{
"created_at": "2012-07-21T07:30:56Z",
"event_name": "user_add_to_team",
"project_access": "Master",
"project_id": 74,
"project_name": "StoreCloud",
"project_path": "storecloud",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
}
4. Team Member Removed:
{
"created_at": "2012-07-21T07:30:56Z",
"event_name": "user_remove_from_team",
"project_access": "Master",
"project_id": 74,
"project_name": "StoreCloud",
"project_path": "storecloud",
"owner_email": "johnsmith@gmail.com",
"owner_name": "John Smith",
}
5. User created:
{
"created_at": "2012-07-21T07:44:07Z",
"email": "js@gitlabhq.com",
"event_name": "user_create",
"name": "John Smith"
}
6. User removed:
{
"created_at": "2012-07-21T07:44:07Z",
"email": "js@gitlabhq.com",
"event_name": "user_destroy",
"name": "John Smith"
}
eos
%>
<% js_lexer = Pygments::Lexer[:js] %>
<%= raw js_lexer.highlight(data_ex_str) %>

View file

@ -0,0 +1,39 @@
.alert.alert-info
%span
Post receive hooks for binding events.
%br
Read more about system hooks
%strong #{link_to "here", help_system_hooks_path, :class => "vlink"}
= form_for @hook, :as => :hook, :url => admin_hooks_path do |f|
-if @hook.errors.any?
.alert-message.block-message.error
- @hook.errors.full_messages.each do |msg|
%p= msg
.clearfix
= f.label :url, "URL:"
.input
= f.text_field :url, :class => "text_field xxlarge"
&nbsp;
= f.submit "Add System Hook", :class => "btn primary"
%hr
-if @hooks.any?
%h3
Hooks
%small (#{@hooks.count})
%br
%table.admin-table
%tr
%th URL
%th Method
%th
- @hooks.each do |hook|
%tr
%td
= link_to admin_hook_path(hook) do
%strong= hook.url
= link_to 'Test Hook', admin_hook_test_path(hook), :class => "btn small right"
%td POST
%td
= link_to 'Remove', admin_hook_path(hook), :confirm => 'Are you sure?', :method => :delete, :class => "danger btn small right"

View file

@ -1,6 +1,9 @@
%h4
%i.icon-file
githost.log
%pre.logs
- Gitlab::Logger.read_latest.each do |line|
%span.log= line
.file_holder#README
.file_title
%i.icon-file
githost.log
.file_content.logs
%ol
- Gitlab::Logger.read_latest.each do |line|
%li
%p= line

View file

@ -1,28 +0,0 @@
%p This is page with preview for all system emails that are sent to user
%p Email previews built based on existing Project/Commit/Issue base - so some preview maybe unavailable unless object appear in system
#accordion
%h3
%a New user
%div
%iframe{ :src=> admin_mailer_preview_user_new_path, :width=>"100%", :height=>"350"}
%h3
%a New issue
%div
%iframe{ :src=> admin_mailer_preview_issue_new_path, :width=>"100%", :height=>"350"}
%h3
%a Commit note
%div
%iframe{ :src=> admin_mailer_preview_note_path(:type => "Commit"), :width=>"100%", :height=>"350"}
%h3
%a Issue note
%div
%iframe{ :src=> admin_mailer_preview_note_path(:type => "Issue"), :width=>"100%", :height=>"350"}
%h3
%a Wall note
%div
%iframe{ :src=> admin_mailer_preview_note_path(:type => "Wall"), :width=>"100%", :height=>"350"}
:javascript
$(function() {
$("#accordion").accordion(); });

View file

@ -13,8 +13,8 @@
%th Team Members
%th Post Receive
%th Last Commit
%th
%th
%th Edit
%th.cred Danger Zone!
- @admin_projects.each do |project|
%tr
@ -24,5 +24,5 @@
%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= link_to 'Destroy', [:admin, project], :confirm => 'Are you sure?', :method => :delete, :class => "btn small danger"
%td.bgred= link_to 'Destroy', [:admin, project], :confirm => "REMOVE #{project.name}? Are you sure?", :method => :delete, :class => "btn small danger"
= paginate @admin_projects, :theme => "admin"

View file

@ -50,7 +50,7 @@
.alert
.clearfix
%p Give user ability to manage application.
%p Make the user a GitLab administrator.
= f.label :admin, :class => "checkbox" do
= f.check_box :admin
%span Administrator
@ -59,11 +59,11 @@
- if @admin_user.blocked
%span
= link_to 'Unblock', unblock_admin_user_path(@admin_user), :method => :put, :class => "btn small"
This user is blocked and is not able to login GitLab
This user is blocked and is not able to login to GitLab
- else
%span
= link_to 'Block', block_admin_user_path(@admin_user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger"
Blocked user will removed from all projects &amp; will not be able to login to GitLab.
Blocked users will be removed from all projects &amp; will not be able to login to GitLab.
.actions
= f.submit 'Save', :class => "btn primary"
- if @admin_user.new_record?

View file

@ -27,7 +27,7 @@
%th Projects
%th Edit
%th Blocked
%th
%th.cred Danger Zone!
- @admin_users.each do |user|
%tr
@ -41,6 +41,6 @@
= link_to 'Unblock', unblock_admin_user_path(user), :method => :put, :class => "btn small success"
- else
= link_to 'Block', block_admin_user_path(user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger"
%td= link_to 'Destroy', [:admin, user], :confirm => 'USER WILL BE REMOVED! Are you sure?', :method => :delete, :class => "btn small danger"
%td.bgred= link_to 'Destroy', [:admin, user], :confirm => "USER #{user.name} WILL BE REMOVED! Are you sure?", :method => :delete, :class => "btn small danger"
= paginate @admin_users, :theme => "admin"

View file

@ -1,4 +1,6 @@
- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits|
%div.ui-box
%h5= day.stamp("28 Aug, 2010")
%h5.small
%i.icon-calendar
= day.stamp("28 Aug, 2010")
%ul.unstyled= render commits

View file

@ -35,7 +35,13 @@
- if file.text?
= render "commits/text_file", :diff => diff, :index => i
- elsif file.image?
.diff_file_content_image{:class => image_diff_class(diff)}
%img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- if diff.renamed_file || diff.new_file || diff.deleted_file
.diff_file_content_image
%img{:class => image_diff_class(diff), :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else
- old_file = (@commit.prev_commit.tree / diff.old_path)
.diff_file_content_image.img_compared
%img{:class => "diff_image_removed", :src => "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
%img{:class => "diff_image_added", :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else
%p.nothing_here_message No preview for this file type

View file

@ -13,12 +13,12 @@
%li{:class => "#{branches_tab_class}"}
= link_to project_repository_path(@project) do
Branches
%span.number= @project.repo.branch_count
%span.badge= @project.repo.branch_count
%li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"}
= link_to tags_project_repository_path(@project) do
Tags
%span.number= @project.repo.tag_count
%span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token

View file

@ -20,7 +20,7 @@
= "..."
= text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge"
.actions
= submit_tag "Compare", :class => "btn primary"
= submit_tag "Compare", :class => "btn btn-primary"
- unless @commits.empty?

View file

@ -8,17 +8,10 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
@events.each do |event|
if event.allowed?
event = EventDecorator.decorate(event)
xml.entry do
if event.issue?
event_link = project_issue_url(event.project, event.issue)
event_title = event.issue_title
elsif event.merge_request?
event_link = project_merge_request_url(event.project, event.merge_request)
event_title = event.merge_request_title
elsif event.push?
event_link = project_commits_url(event.project, :ref => event.ref_name)
event_title = event.ref_name
end
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

View file

@ -10,9 +10,10 @@
add new key
to your profile
- if @events.any?
= render @events
.content_list= render @events
- else
%h4.nothing_here_message Projects activity will be displayed here
.loading.hide
.side
= render "events/event_last_push", :event => @last_push
.projects_box
@ -54,3 +55,7 @@
New Project »
- else
If you will be added to project - it will be displayed here
:javascript
$(function(){ Pager.init(20); });

View file

@ -1,2 +1,2 @@
:plain
$(".projects .activities").append("#{escape_javascript(render(@events))}");
Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");

View file

@ -1,7 +1,7 @@
= image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name}
%span.event_label= event.action_name
&nbsp;issue
%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

View file

@ -5,12 +5,9 @@
%span Your pushed to
= event.ref_type
= link_to project_commits_path(event.project, :ref => event.ref_name) do
%strong= event.ref_name
%strong= truncate(event.ref_name, :length => 28)
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
= link_to new_mr_path_from_push_event(event), :title => "New Merge Request", :class => "btn very_small primary" do
Create Merge Request

View file

@ -2,8 +2,8 @@
.event_icon= image_tag "event_mr_merged.png"
= image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name}
%span.event_label= event.action_name
&nbsp;merge request
%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

View file

@ -2,7 +2,7 @@
.event_icon= image_tag "event_push.png"
= image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name}
%span.event_label= event.push_action_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

View file

@ -0,0 +1,41 @@
%h3 API
.back_link
= link_to help_path do
&larr; to index
%hr
%ol
%li
%a{:href => "#README"} README
%li
%a{:href => "#projects"} Projects
%li
%a{:href => "#users"} Users
.file_holder#README
.file_title
%i.icon-file
README
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "README.md"))
%br
.file_holder#projects
.file_title
%i.icon-file
Projects
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "projects.md"))
%br
.file_holder#users
.file_title
%i.icon-file
Users
.file_content.wiki
= preserve do
= markdown File.read(Rails.root.join("doc", "api", "users.md"))

View file

@ -22,3 +22,9 @@
%li
%h5= link_to "Web Hooks", help_web_hooks_path
%li
%h5= link_to "System Hooks", help_system_hooks_path
%li
%h5= link_to "API", help_api_path

View file

@ -0,0 +1,13 @@
%h3 System hooks
.back_link
= link_to :back do
&larr; back
%hr
%p.slead
Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
%br
System Hooks can be used for logging or change information in LDAP server.
%br
%h5 Hooks request example:
= render "admin/hooks/data_ex"

View file

@ -6,7 +6,9 @@
.row
.span7= paginate @issues, :remote => true, :theme => "gitlab"
.span3.right
%span.cgray.right #{@issues.total_count} issues for this filter
%span.cgray.right
%span.issue_counter #{@issues.total_count}
issues for this filter
- else
%li
%h4.nothing_here_message Nothing to show here

View file

@ -12,9 +12,9 @@
= issue.notes.count
- if can? current_user, :modify_issue, issue
- if issue.closed
= link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped", :remote => true
= link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped reopen_issue", :remote => true
- else
= link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped", :remote => true
= link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped close_issue", :remote => true
= link_to edit_project_issue_path(issue.project, issue), :class => "btn small edit-issue-link", :remote => true do
%i.icon-edit
Edit
@ -35,6 +35,4 @@
&nbsp;
- if issue.upvotes > 0
%span.badge.badge-success= "+#{issue.upvotes}"
%span.badge.badge-success= "+#{issue.upvotes}"

View file

@ -2,7 +2,7 @@
.issues_content
%h3.page_title
Issues
%small (#{@issues.total_count})
%small (<span class=issue_counter>#{@issues.total_count}</span>)
.right
.span5
- if can? current_user, :write_issue, @project
@ -45,4 +45,4 @@
:javascript
$(function(){
issuesPage();
})
})

View file

@ -1,4 +1,4 @@
%h3 New key
%h3.page_title New key
%hr
= render 'form'
@ -11,4 +11,4 @@
if( key_mail && key_mail.length > 0 && title.val() == '' ){
$('#key_title').val( key_mail );
}
});
});

View file

@ -17,14 +17,14 @@
%li{:class => tab_class(:issues)}
= link_to project_issues_filter_path(@project) do
Issues
%span.count= @project.issues.opened.count
%span.count.issue_counter= @project.issues.opened.count
- if @project.repo_exists?
- if @project.merge_requests_enabled
%li{:class => tab_class(:merge_requests)}
= link_to project_merge_requests_path(@project) do
Merge Requests
%span.count= @project.merge_requests.opened.count
%span.count.merge_counter= @project.merge_requests.opened.count
- if @project.wall_enabled
%li{:class => tab_class(:wall)}

View file

@ -15,7 +15,7 @@
%li{:class => tab_class(:admin_logs)}
= link_to "Logs", admin_logs_path
%li{:class => tab_class(:admin_emails)}
= link_to "Emails", admin_emails_path
= link_to "Hooks", admin_hooks_path
%li{:class => tab_class(:admin_resque)}
= link_to "Resque", admin_resque_path

View file

@ -12,16 +12,17 @@
%li{:class => tab_class(:password)}
= link_to "Password", profile_password_path
%li{:class => tab_class(:ssh_keys)}
= link_to keys_path do
SSH Keys
%span.count= current_user.keys.count
%li{:class => tab_class(:token)}
= link_to "Token", profile_token_path
%li{:class => tab_class(:design)}
= link_to "Design", profile_design_path
%li{:class => tab_class(:ssh_keys)}
= link_to keys_path do
SSH Keys
%span.count= current_user.keys.count
.content
= yield

View file

@ -5,7 +5,8 @@
- @merge_request.errors.full_messages.each do |msg|
%li= msg
%h3.padded.cgray 1. Select Branches
%h4.cdark 1. Select Branches
%br
.row
.span6
@ -30,14 +31,21 @@
.bottom_commit
.mr_target_commit
%h3.padded.cgray 2. Fill info
%h4.cdark 2. Fill info
.clearfix
= f.label :assignee_id, "Assign to", :class => "control-label"
.controls= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px")
.main_box
.top_box_content
= f.label :title do
%strong= "Title *"
.input= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5
.middle_box_content
= f.label :assignee_id do
%i.icon-user
Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px")
.control-group
= f.label :title, :class => "control-label"
.controls= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5
.form-actions
= f.submit 'Save', :class => "btn-primary btn"

View file

@ -15,12 +15,14 @@
&rarr;
= merge_request.target_branch
= image_tag gravatar_icon(merge_request.author_email), :class => "avatar"
= link_to project_merge_request_path(merge_request.project, merge_request) do
%p.row_title= truncate(merge_request.title, :length => 80)
%span.update-author
%strong= merge_request.author_name
authored
%small.cdark= "##{merge_request.id}"
authored by #{merge_request.author_name}
= time_ago_in_words(merge_request.created_at)
ago
- if merge_request.upvotes > 0
%span.badge.badge-success= "+#{merge_request.upvotes}"
= link_to project_merge_request_path(merge_request.project, merge_request) do
%p.row_title= truncate(merge_request.title, :length => 80)

View file

@ -1,4 +1,4 @@
%h3
%h3.page_title
= "Edit merge request #{@merge_request.id}"
%hr
= render 'form'

View file

@ -1,3 +1,3 @@
%h3 New Merge Request
%h3.page_title New Merge Request
%hr
= render 'form'

View file

@ -1,6 +1,8 @@
- if @commits.present?
.ui-box
%h5 Commits (#{@commits.count})
%h5
%i.icon-list
Commits (#{@commits.count})
.merge-request-commits
- if @commits.count > 8
%ul.first_mr_commits.unstyled

View file

@ -13,9 +13,10 @@
= image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_author(@merge_request)
%cite.cgray and currently assigned to
= image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_assignee(@merge_request)
- if @merge_request.assignee
%cite.cgray and currently assigned to
= image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_assignee(@merge_request)
- if @merge_request.closed

View file

@ -32,4 +32,4 @@
%span Any file less than 10 MB
= f.submit 'Add Comment', :class => "btn primary", :id => "submit_note"
= f.submit 'Add Comment', :class => "btn primary submit_note", :id => "submit_note"

View file

@ -24,7 +24,7 @@
= check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
%span Commit author
.actions
= f.submit 'Add note', :class => "btn primary", :id => "submit_note"
= f.submit 'Add note', :class => "btn primary submit_note", :id => "submit_note"
= link_to "Close", "#", :class => "btn hide-button"
:javascript

View file

@ -1,3 +1,4 @@
%tr.line_notes_row.reply
%td{:colspan => 3}
%i.icon-comment
= link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line"

View file

@ -13,7 +13,7 @@
= render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree }
- else
- contents = tree.contents
%table#tree-slider.bordered-table.table
%table#tree-slider.bordered-table.table{:class => "table_#{@hex_path}" }
%thead
%th Name
%th Last Update
@ -29,34 +29,39 @@
%td
%td
- index = 0
- contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content|
= render :partial => "refs/tree_item", :locals => { :content => content }
= render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) }
- contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content|
= render :partial => "refs/tree_item", :locals => { :content => content }
= render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) }
- contents.select{ |i| i.is_a?(Grit::Submodule)}.each do |content|
= render :partial => "refs/submodule_item", :locals => { :content => content }
= render :partial => "refs/submodule_item", :locals => { :content => content, :index => (index += 1) }
- if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first
#tree-readme-holder
%h3= content.name
.readme
.file_holder#README
.file_title
%i.icon-file
= content.name
.file_content.wiki
- if content.name =~ /\.(md|markdown)$/i
= preserve do
= markdown(content.data)
- else
= simple_format(content.data)
- if params[:path]
- history_path = tree_file_project_ref_path(@project, @ref, params[:path])
- else
- history_path = tree_project_ref_path(@project, @ref)
:javascript
$(function(){
$('select#branch').selectmenu({style:'popup', width:200});
$('select#tag').selectmenu({style:'popup', width:200});
$('.project-refs-select').chosen();
history.pushState({ path: this.path }, '', "#{history_path}")
history.pushState({ path: this.path }, '', "#{@history_path}");
});
// Load last commit log for each file in tree
$(window).load(function(){
ajaxGet('#{@logs_path}');
});

View file

@ -0,0 +1,3 @@
- if tm
%strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
= link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"

View file

@ -1,5 +1,5 @@
.view_file
.view_file_header
.file_holder
.file_title
%i.icon-file
%span.file_name
= name
@ -10,26 +10,28 @@
= link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
- if file.text?
- if name =~ /\.(md|markdown)$/i
#tree-readme-holder
.readme
= preserve do
= markdown(file.data)
.file_content.wiki
= preserve do
= markdown(file.data)
- else
.view_file_content
.file_content.code
- unless file.empty?
%div{:class => current_user.dark_scheme ? "black" : "white"}
= preserve do
= raw file.colorize(options: { linenos: 'True'})
- else
%h4.nothing_here_message Empty file
- elsif file.image?
.view_file_content_image
.file_content.image_file
%img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else
%center
= link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do
%div.padded
%br
= image_tag "download.png", :width => 64
%h3
Download (#{file.mb_size})
.file_content.blob_file
%center
= link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do
%div.padded
%br
= image_tag "download.png", :width => 64
%h3
Download (#{file.mb_size})

View file

@ -1,23 +1,11 @@
- file = params[:path] ? File.join(params[:path], content.name) : content.name
- content_commit = @project.commits(@commit.id, file, 1).last
- return unless content_commit
%tr{ :class => "tree-item", :url => tree_file_project_ref_path(@project, @ref, file) }
- file = tree_full_path(content)
%tr{ :class => "tree-item #{tree_hex_class(content)}", :url => tree_file_project_ref_path(@project, @ref, file) }
%td.tree-item-file-name
- if content.is_a?(Grit::Blob)
- if content.text?
= image_tag "file_txt.png"
- elsif content.image?
= image_tag "file_img.png"
- else
= image_tag "file_bin.png"
- else
= image_tag "file_dir.png"
= tree_icon(content)
= link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true
%td.cgray
= time_ago_in_words(content_commit.committed_date)
ago
%td.commit
- tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)
- if tm
%strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
= link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
%td.tree_time_ago.cgray
- if index == 1
%span.log_loading
Loading commit data..
= image_tag "ajax_loader_tree.gif", :width => 14
%td.tree_commit

View file

@ -11,8 +11,8 @@
%li= link
.clear
.view_file.blame_file
.view_file_header
.file_holder
.file_title
%i.icon-file
%span.file_name
= @tree.name
@ -21,7 +21,7 @@
= 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"
.view_file_content
.file_content.blame
%table
- @blame.each do |commit, lines|
- commit = Commit.new(commit)
@ -29,7 +29,7 @@
%td.author
= image_tag gravatar_icon(commit.author_email, 16)
= commit.author_name
%td.commit
%td.blame_commit
&nbsp;
= link_to project_commit_path(@project, :id => commit.id) do
%code= commit.id.to_s[0..10]
@ -37,8 +37,7 @@
%td.lines
= preserve do
%pre
- lines.each do |line|
= line
= Gitlab::Encode.utf8 lines.join("\n")
:javascript
$(function(){

View file

@ -0,0 +1,9 @@
- @logs.each do |content_data|
- file_name = content_data[:file_name]
- content_commit = content_data[:commit]
- tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)
:plain
var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}");
row.find("td.tree_time_ago").html('#{escape_javascript(time_ago_in_words(content_commit.committed_date))} ago');
row.find("td.tree_commit").html('#{escape_javascript(render("tree_commit", :tm => tm, :content_commit => content_commit))}');

View file

@ -1,4 +1,10 @@
:plain
// Load Files list
$("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}");
$("#tree-content-holder").show("slide", { direction: "right" }, 150);
$('.project-refs-form #path').val("#{params[:path]}");
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}');
});

View file

@ -7,16 +7,14 @@
= link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right"
%br
#tree-holder
#tree-content-holder
.view_file
.view_file_header
%i.icon-file
%strong= @snippet.file_name
%span.options
= link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"
.view_file_content
%div{:class => current_user.dark_scheme ? "black" : ""}
= raw @snippet.colorize(options: { linenos: 'True'})
.file_holder
.file_title
%i.icon-file
%strong= @snippet.file_name
%span.options
= link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"
.file_content.code
%div{:class => current_user.dark_scheme ? "black" : ""}
= raw @snippet.colorize(options: { linenos: 'True'})
= render "notes/notes", :tid => @snippet.id, :tt => "snippet"

View file

@ -0,0 +1,7 @@
class SystemHookWorker
@queue = :system_hook
def self.perform(hook_id, data)
SystemHook.find(hook_id).execute data
end
end

View file

@ -23,7 +23,7 @@ module Gitlab
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer
config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer, :system_hook_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.

View file

@ -21,6 +21,8 @@ email:
# Like default project limit for user etc
app:
default_projects_limit: 10
# backup_path: "/vol/backups" # default: Rails.root + backups/
# backup_keep_time: 604800 # default: 0 (forever) (in seconds)
#

View file

@ -95,11 +95,21 @@ class Settings < Settingslogic
end
def gitolite_admin_uri
git['admin_uri'] || 'git@localhost:gitolite-admin'
git_host['admin_uri'] || 'git@localhost:gitolite-admin'
end
def default_projects_limit
app['default_projects_limit'] || 10
end
def backup_path
t = app['backup_path'] || "backups/"
t = /^\//.match(t) ? t : File.join(Rails.root + t)
t
end
def backup_keep_time
app['backup_keep_time'] || 0
end
end
end

Some files were not shown because too many files have changed in this diff Show more