Commit eca823c1 authored by Nihad Abbasov's avatar Nihad Abbasov

Merge branch 'master' into api

parents 024e0348 8b7e404b
...@@ -6,6 +6,7 @@ log/*.log ...@@ -6,6 +6,7 @@ log/*.log
tmp/ tmp/
.sass-cache/ .sass-cache/
coverage/* coverage/*
backups/*
*.swp *.swp
public/uploads/ public/uploads/
.rvmrc .rvmrc
......
v 2.7.0 v 2.7.0
- Issue Labels - Issue Labels
- Inline diff
- Git HTTP
- API
- UI improved
- System hooks
- UI improved
- Dashboard events endless scroll
- Source perfomance increased
v 2.6.0 v 2.6.0
- UI polished - UI polished
......
...@@ -7,7 +7,7 @@ gem "sqlite3" ...@@ -7,7 +7,7 @@ gem "sqlite3"
gem "mysql2" gem "mysql2"
# Auth # Auth
gem "devise", "~> 1.5" gem "devise", "~> 2.1.0"
# GITLAB patched libs # GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
...@@ -71,7 +71,6 @@ group :development, :test do ...@@ -71,7 +71,6 @@ group :development, :test do
gem "awesome_print" gem "awesome_print"
gem "database_cleaner" gem "database_cleaner"
gem "launchy" gem "launchy"
gem "webmock"
end end
group :test do group :test do
...@@ -82,4 +81,5 @@ group :test do ...@@ -82,4 +81,5 @@ group :test do
gem "shoulda-matchers" gem "shoulda-matchers"
gem 'email_spec' gem 'email_spec'
gem 'resque_spec' gem 'resque_spec'
gem "webmock"
end end
...@@ -148,10 +148,11 @@ GEM ...@@ -148,10 +148,11 @@ GEM
nokogiri (>= 1.5.0) nokogiri (>= 1.5.0)
daemons (1.1.8) daemons (1.1.8)
database_cleaner (0.8.0) database_cleaner (0.8.0)
devise (1.5.3) devise (2.1.2)
bcrypt-ruby (~> 3.0) bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.0.3) orm_adapter (~> 0.1)
warden (~> 1.1) railties (~> 3.1)
warden (~> 1.2.1)
diff-lcs (1.1.3) diff-lcs (1.1.3)
drapper (0.8.4) drapper (0.8.4)
email_spec (1.2.1) email_spec (1.2.1)
...@@ -225,7 +226,7 @@ GEM ...@@ -225,7 +226,7 @@ GEM
omniauth (1.1.0) omniauth (1.1.0)
hashie (~> 1.2) hashie (~> 1.2)
rack rack
orm_adapter (0.0.7) orm_adapter (0.3.0)
polyglot (0.3.3) polyglot (0.3.3)
posix-spawn (0.3.6) posix-spawn (0.3.6)
pry (0.9.9.6) pry (0.9.9.6)
...@@ -356,7 +357,7 @@ GEM ...@@ -356,7 +357,7 @@ GEM
raindrops (~> 0.7) raindrops (~> 0.7)
vegas (0.1.11) vegas (0.1.11)
rack (>= 1.0.0) rack (>= 1.0.0)
warden (1.2.0) warden (1.2.1)
rack (>= 1.0) rack (>= 1.0)
webmock (1.8.7) webmock (1.8.7)
addressable (>= 2.2.7) addressable (>= 2.2.7)
...@@ -383,7 +384,7 @@ DEPENDENCIES ...@@ -383,7 +384,7 @@ DEPENDENCIES
colored colored
cucumber-rails cucumber-rails
database_cleaner database_cleaner
devise (~> 1.5) devise (~> 2.1.0)
drapper drapper
email_spec email_spec
ffaker ffaker
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
//= require jquery.cookie //= require jquery.cookie
//= require jquery.endless-scroll //= require jquery.endless-scroll
//= require jquery.highlight //= require jquery.highlight
//= require jquery.waitforimages
//= require bootstrap-modal //= require bootstrap-modal
//= require modernizr //= require modernizr
//= require chosen-jquery //= require chosen-jquery
...@@ -20,10 +21,26 @@ ...@@ -20,10 +21,26 @@
//= require_tree . //= require_tree .
$(document).ready(function(){ $(document).ready(function(){
$(".one_click_select").live("click", function(){ $(".one_click_select").live("click", function(){
$(this).select(); $(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").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu); $(".account-box").mouseleave(resetMenu);
...@@ -97,3 +114,8 @@ function showDiff(link) { ...@@ -97,3 +114,8 @@ function showDiff(link) {
return _chosen.apply(this, [default_options]); return _chosen.apply(this, [default_options]);
}}) }})
})(jQuery); })(jQuery);
function ajaxGet(url) {
$.ajax({type: "GET", url: url, dataType: "script"});
}
...@@ -73,4 +73,25 @@ function issuesPage(){ ...@@ -73,4 +73,25 @@ function issuesPage(){
$("#milestone_id, #assignee_id, #label_name").on("change", function(){ $("#milestone_id, #assignee_id, #label_name").on("change", function(){
$(this).closest("form").submit(); $(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 );
}
});
});
} }
...@@ -25,11 +25,11 @@ init: ...@@ -25,11 +25,11 @@ init:
$(this).closest('li').fadeOut(); }); $(this).closest('li').fadeOut(); });
$("#new_note").live("ajax:before", function(){ $("#new_note").live("ajax:before", function(){
$("#submit_note").attr("disabled", "disabled"); $(".submit_note").attr("disabled", "disabled");
}) })
$("#new_note").live("ajax:complete", function(){ $("#new_note").live("ajax:complete", function(){
$("#submit_note").removeAttr("disabled"); $(".submit_note").removeAttr("disabled");
}) })
$("#note_note").live("focus", function(){ $("#note_note").live("focus", function(){
......
...@@ -604,7 +604,11 @@ li.note { ...@@ -604,7 +604,11 @@ li.note {
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
@include border-radius(4px); @include border-radius(4px);
min-height:42px; min-height:22px;
.avatar {
width:24px;
}
} }
.supp_diff_link, .supp_diff_link,
......
...@@ -202,6 +202,10 @@ a:focus { ...@@ -202,6 +202,10 @@ a:focus {
color:$style_color; color:$style_color;
} }
.nav-tabs > .active > a {
font-weight:bold;
}
/** COLORS **/ /** COLORS **/
.cgray { color:gray; } .cgray { color:gray; }
.cred { color:#D12F19; } .cred { color:#D12F19; }
...@@ -209,6 +213,7 @@ a:focus { ...@@ -209,6 +213,7 @@ a:focus {
.cblack { color:#111; } .cblack { color:#111; }
.cdark { color:#444 } .cdark { color:#444 }
.cwhite { color:#fff !important } .cwhite { color:#fff !important }
.bgred { background: #F2DEDE !important}
/** COMMON STYLES **/ /** COMMON STYLES **/
.left { .left {
...@@ -299,9 +304,24 @@ table.no-borders { ...@@ -299,9 +304,24 @@ table.no-borders {
} }
.event_label { .event_label {
background: #FCEEC1; @extend .label;
padding: 2px 2px 0; background-color: #999;
font-family: monospace;
&.pushed {
background-color: #3A87AD;
}
&.opened {
background-color: #468847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
} }
img.avatar { img.avatar {
...@@ -425,9 +445,10 @@ form { ...@@ -425,9 +445,10 @@ form {
*/ */
.ui-box { .ui-box {
background:#F9F9F9; background:#F9F9F9;
margin-bottom: 40px; margin-bottom: 25px;
@include round-borders-all(4px); @include round-borders-all(4px);
border-color: #CCC; border-color: #CCC;
@include solid_shade;
ul { ul {
margin:0; margin:0;
...@@ -443,6 +464,13 @@ form { ...@@ -443,6 +464,13 @@ form {
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-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 { form {
padding:9px 0; padding:9px 0;
margin:0px; margin:0px;
...@@ -511,6 +539,7 @@ form { ...@@ -511,6 +539,7 @@ form {
table.admin-table { table.admin-table {
@extend .table-bordered; @extend .table-bordered;
@extend .zebra-striped; @extend .zebra-striped;
@include solid_shade;
th { th {
border-color: #CCC; border-color: #CCC;
border-bottom: 1px solid #bbb; border-bottom: 1px solid #bbb;
...@@ -568,6 +597,8 @@ ul.breadcrumb { ...@@ -568,6 +597,8 @@ ul.breadcrumb {
@extend .prepend-top-20; @extend .prepend-top-20;
@extend .append-bottom-20; @extend .append-bottom-20;
border-width:1px; border-width:1px;
@include solid_shade;
img { max-width: 100%; } img { max-width: 100%; }
...@@ -624,13 +655,166 @@ p { ...@@ -624,13 +655,166 @@ p {
h3.page_title { h3.page_title {
color:#456; color:#456;
font-size:20px; font-size:20px;
font-weight: 600; font-weight: normal;
line-height: 28px; line-height: 28px;
} }
pre.logs { /**
.log { * File content holder
font-size:12px; *
line-height:18px; */
.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;
}
}
} }
} }
...@@ -96,7 +96,7 @@ header { ...@@ -96,7 +96,7 @@ header {
*/ */
.search { .search {
float: right; float: right;
margin-right: 55px; margin-right: 50px;
.search-input { .search-input {
@extend .span2; @extend .span2;
...@@ -126,10 +126,10 @@ header { ...@@ -126,10 +126,10 @@ header {
cursor: pointer; cursor: pointer;
img { img {
border-radius: 4px; border-radius: 4px;
right: 0px; right: 5px;
position: absolute; position: absolute;
width: 33px; width: 31px;
height: 33px; height: 31px;
display: block; display: block;
top: 0; top: 0;
&:after { &:after {
......
...@@ -31,6 +31,12 @@ $hover: #FDF5D9; ...@@ -31,6 +31,12 @@ $hover: #FDF5D9;
box-shadow: 0 0 3px #ddd; 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) { @mixin border-radius($radius) {
-moz-border-radius: $radius; -moz-border-radius: $radius;
-webkit-border-radius: $radius; -webkit-border-radius: $radius;
...@@ -136,7 +142,7 @@ $hover: #FDF5D9; ...@@ -136,7 +142,7 @@ $hover: #FDF5D9;
/** /**
* Code (files list) styles. Browsing project files there * Code (files list) styles. Browsing project files there
*/ */
@import "tree.scss"; @import "sections/tree.scss";
/** /**
* This file represent notes(comments) styles * This file represent notes(comments) styles
......
...@@ -63,18 +63,22 @@ p.notify_controls span{ ...@@ -63,18 +63,22 @@ p.notify_controls span{
tr.line_notes_row { tr.line_notes_row {
border-bottom:1px solid #DDD; border-bottom:1px solid #DDD;
border-left: 7px solid #2A79A3;
&.reply { &.reply {
background:#eee; background:#eee;
border-left: 7px solid #2A79A3;
border-top:1px solid #ddd;
td { td {
padding:7px 10px; padding:7px 10px;
} }
a.line_note_reply_link { a.line_note_reply_link {
@include round-borders-all(4px); @include round-borders-all(4px);
border-color:#aaa; padding: 3px 10px;
background: #bbb; margin-left:5px;
padding: 3px 20px;
color: white; color: white;
background: #2A79A3;
border-color: #2A79A3;
} }
} }
ul { ul {
...@@ -95,6 +99,9 @@ tr.line_notes_row { ...@@ -95,6 +99,9 @@ tr.line_notes_row {
td { td {
border-bottom:1px solid #ddd; border-bottom:1px solid #ddd;
} }
.actions {
margin:0;
}
} }
td .line_note_link { td .line_note_link {
......
...@@ -101,18 +101,21 @@ ...@@ -101,18 +101,21 @@
margin:50px; margin:50px;
padding:1px; padding:1px;
max-width:400px; max-width:400px;
}
&.diff_image_removed { &.diff_image_removed {
img {
border: 1px solid #C00; border: 1px solid #C00;
} }
}
&.diff_image_added { &.diff_image_added {
img {
border: 1px solid #0C0;; border: 1px solid #0C0;;
} }
} }
&.img_compared {
img {
max-width:300px;
}
}
} }
} }
......
...@@ -82,3 +82,15 @@ ...@@ -82,3 +82,15 @@
} }
} }
} }
li.merge_request {
padding:7px 10px;
img.avatar {
width: 32px;
margin-top: 4px;
}
p {
padding: 0px;
padding-bottom: 2px;
}
}
...@@ -25,103 +25,6 @@ ...@@ -25,103 +25,6 @@
} }
} }
/** 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 { #tree-slider {
@include border-radius(0); @include border-radius(0);
.tree-item { .tree-item {
...@@ -152,7 +55,7 @@ ...@@ -152,7 +55,7 @@
#tree-slider { #tree-slider {
@include shade; @include solid_shade;
width:100%; width:100%;
border-color:#ccc; border-color:#ccc;
...@@ -183,21 +86,6 @@ ...@@ -183,21 +86,6 @@
color:#333; 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 { a.tree-commit-link {
color: #666; color: #666;
&:hover { &:hover {
...@@ -206,27 +94,3 @@ ...@@ -206,27 +94,3 @@
} }
} }
.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;
}
}
}
}
...@@ -70,8 +70,7 @@ ...@@ -70,8 +70,7 @@
} }
} }
.separator { .separator {
border-color:#444; display:none;
background:#31363E;
} }
} }
......
class BaseContext
attr_accessor :project, :current_user, :params
def initialize(project, user, params)
@project, @current_user, @params = project, user, params.dup
end
end
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
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
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
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
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
...@@ -6,7 +6,7 @@ class Admin::ProjectsController < ApplicationController ...@@ -6,7 +6,7 @@ class Admin::ProjectsController < ApplicationController
def index def index
@admin_projects = Project.scoped @admin_projects = Project.scoped
@admin_projects = @admin_projects.search(params[:name]) if params[:name].present? @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 end
def show def show
...@@ -72,6 +72,6 @@ class Admin::ProjectsController < ApplicationController ...@@ -72,6 +72,6 @@ class Admin::ProjectsController < ApplicationController
@admin_project = Project.find_by_code(params[:id]) @admin_project = Project.find_by_code(params[:id])
@admin_project.destroy @admin_project.destroy
redirect_to admin_projects_url redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
end end
end end
...@@ -52,7 +52,7 @@ class ApplicationController < ActionController::Base ...@@ -52,7 +52,7 @@ class ApplicationController < ActionController::Base
def layout_by_resource def layout_by_resource
if devise_controller? if devise_controller?
"devise" "devise_layout"
else else
"application" "application"
end end
......
...@@ -26,43 +26,31 @@ class CommitsController < ApplicationController ...@@ -26,43 +26,31 @@ class CommitsController < ApplicationController
end end
def show 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) if @commit
@suppress_diff = result[:suppress_diff]
@note = @project.build_commit_note(@commit) @note = result[:note]
@comments_allowed = true @line_notes = result[:line_notes]
@line_notes = project.commit_line_notes(@commit) @notes_count = result[:notes_count]
@comments_allowed = true
@notes_count = @line_notes.count + project.commit_notes(@commit).count else
return git_not_found!
if @commit.diffs.size > 200 && !params[:force_show_diff]
@suppress_diff = true
end end
rescue Grit::Git::GitTimeout rescue Grit::Git::GitTimeout
render "huge_commit" render "huge_commit"
end end
def compare def compare
first = project.commit(params[:to].try(:strip)) result = Commit.compare(project, params[:from], params[:to])
last = project.commit(params[:from].try(:strip))
@diffs = [] @commits = result[:commits]
@commits = [] @commit = result[:commit]
@diffs = result[:diffs]
@line_notes = [] @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 end
def patch def patch
......
...@@ -2,15 +2,13 @@ class DashboardController < ApplicationController ...@@ -2,15 +2,13 @@ class DashboardController < ApplicationController
respond_to :html respond_to :html
def index def index
@projects = current_user.projects.includes(:events).order("events.created_at DESC") @projects = current_user.projects_with_events.page(params[:page]).per(40)
@projects = @projects.page(params[:page]).per(40) @events = Event.recent_for_user(current_user).limit(20).offset(params[:offset] || 0)
@events = Event.where(:project_id => current_user.projects.map(&:id)).recent.limit(20)
@last_push = current_user.recent_push @last_push = current_user.recent_push
respond_to do |format| respond_to do |format|
format.html format.html
format.js
format.atom { render :layout => false } format.atom { render :layout => false }
end end
end end
......
...@@ -11,24 +11,24 @@ class HooksController < ApplicationController ...@@ -11,24 +11,24 @@ class HooksController < ApplicationController
respond_to :html respond_to :html
def index def index
@hooks = @project.web_hooks.all @hooks = @project.hooks.all
@hook = WebHook.new @hook = ProjectHook.new
end end
def create def create
@hook = @project.web_hooks.new(params[:hook]) @hook = @project.hooks.new(params[:hook])
@hook.save @hook.save
if @hook.valid? if @hook.valid?
redirect_to project_hooks_path(@project) redirect_to project_hooks_path(@project)
else else
@hooks = @project.web_hooks.all @hooks = @project.hooks.all
render :index render :index
end end
end end
def test def test
@hook = @project.web_hooks.find(params[:id]) @hook = @project.hooks.find(params[:id])
commits = @project.commits(@project.default_branch, nil, 3) 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) data = @project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}", current_user)
@hook.execute(data) @hook.execute(data)
...@@ -37,7 +37,7 @@ class HooksController < ApplicationController ...@@ -37,7 +37,7 @@ class HooksController < ApplicationController
end end
def destroy def destroy
@hook = @project.web_hooks.find(params[:id]) @hook = @project.hooks.find(params[:id])
@hook.destroy @hook.destroy
redirect_to project_hooks_path(@project) redirect_to project_hooks_path(@project)
......
...@@ -24,16 +24,7 @@ class MergeRequestsController < ApplicationController ...@@ -24,16 +24,7 @@ class MergeRequestsController < ApplicationController
def index def index
@merge_requests = @project.merge_requests @merge_requests = MergeRequestsLoad.new(project, current_user, params).execute
@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")
end end
def show def show
......
...@@ -40,25 +40,6 @@ class NotesController < ApplicationController ...@@ -40,25 +40,6 @@ class NotesController < ApplicationController
protected protected
def notes def notes
@notes = case params[:target_type] @notes = NotesLoad.new(project, current_user, params).execute
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
end end
end end
class OmniauthCallbacksController < Devise::OmniauthCallbacksController 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 def ldap
# We only find ourselves here if the authentication to LDAP was successful. # We only find ourselves here if the authentication to LDAP was successful.
......
...@@ -9,7 +9,7 @@ class RefsController < ApplicationController ...@@ -9,7 +9,7 @@ class RefsController < ApplicationController
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :ref before_filter :ref
before_filter :define_tree_vars, :only => [:tree, :blob, :blame] before_filter :define_tree_vars, :only => [:tree, :blob, :blame, :logs_tree]
before_filter :render_full_content before_filter :render_full_content
layout "project" layout "project"
...@@ -46,6 +46,18 @@ class RefsController < ApplicationController ...@@ -46,6 +46,18 @@ class RefsController < ApplicationController
end end
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 def blob
if @tree.is_blob? if @tree.is_blob?
if @tree.text? if @tree.text?
...@@ -79,6 +91,15 @@ class RefsController < ApplicationController ...@@ -79,6 +91,15 @@ class RefsController < ApplicationController
@commit = project.commit(@ref) @commit = project.commit(@ref)
@tree = Tree.new(@commit.tree, project, @ref, params[:path]) @tree = Tree.new(@commit.tree, project, @ref, params[:path])
@tree = TreeDecorator.new(@tree) @tree = TreeDecorator.new(@tree)
@hex_path = Digest::SHA1.hexdigest(params[:path] || "/")
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 rescue
return render_404 return render_404
end end
......
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
...@@ -134,4 +134,8 @@ module ApplicationHelper ...@@ -134,4 +134,8 @@ module ApplicationHelper
end end
active ? "current" : nil active ? "current" : nil
end end
def hexdigest(string)
Digest::SHA1.hexdigest string
end
end 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"
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
...@@ -80,6 +80,29 @@ class Commit ...@@ -80,6 +80,29 @@ class Commit
def commits_between(repo, from, to) def commits_between(repo, from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) } repo.commits_between(from, to).map { |c| Commit.new(c) }
end 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 end
def persisted? def persisted?
......
...@@ -28,6 +28,10 @@ class Event < ActiveRecord::Base ...@@ -28,6 +28,10 @@ class Event < ActiveRecord::Base
end end
end end
def self.recent_for_user user
where(:project_id => user.projects.map(&:id)).recent
end
# Next events currently enabled for system # Next events currently enabled for system
# - push # - push
# - new issue # - new issue
......
...@@ -22,7 +22,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -22,7 +22,6 @@ class MergeRequest < ActiveRecord::Base
:should_remove_source_branch :should_remove_source_branch
validates_presence_of :project_id validates_presence_of :project_id
validates_presence_of :assignee_id
validates_presence_of :author_id validates_presence_of :author_id
validates_presence_of :source_branch validates_presence_of :source_branch
validates_presence_of :target_branch validates_presence_of :target_branch
...@@ -36,6 +35,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -36,6 +35,7 @@ class MergeRequest < ActiveRecord::Base
delegate :name, delegate :name,
:email, :email,
:to => :assignee, :to => :assignee,
:allow_nil => true,
:prefix => true :prefix => true
validates :title, validates :title,
...@@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base
def unmerged_diffs def unmerged_diffs
commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} 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 end
def last_commit def last_commit
......
...@@ -19,7 +19,7 @@ class Project < ActiveRecord::Base ...@@ -19,7 +19,7 @@ class Project < ActiveRecord::Base
has_many :notes, :dependent => :destroy has_many :notes, :dependent => :destroy
has_many :snippets, :dependent => :destroy has_many :snippets, :dependent => :destroy
has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key" 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 :wikis, :dependent => :destroy
has_many :protected_branches, :dependent => :destroy has_many :protected_branches, :dependent => :destroy
...@@ -120,7 +120,7 @@ class Project < ActiveRecord::Base ...@@ -120,7 +120,7 @@ class Project < ActiveRecord::Base
errors.add(:path, " like 'gitolite-admin' is not allowed") errors.add(:path, " like 'gitolite-admin' is not allowed")
end end
end end
def self.access_options def self.access_options
UsersProject.access_roles UsersProject.access_roles
end end
......
class ProjectHook < WebHook
belongs_to :project
end
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
class User < ActiveRecord::Base class User < ActiveRecord::Base
include Account include Account
devise :database_authenticatable, :token_authenticatable, devise :database_authenticatable, :token_authenticatable, :lockable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable :recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password :theme_id, :force_random_password
attr_accessor :force_random_password attr_accessor :force_random_password
...@@ -15,6 +16,11 @@ class User < ActiveRecord::Base ...@@ -15,6 +16,11 @@ class User < ActiveRecord::Base
has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id
has_many :keys, :dependent => :destroy has_many :keys, :dependent => :destroy
has_many :events,
:class_name => "Event",
:foreign_key => :author_id,
:dependent => :destroy
has_many :recent_events, has_many :recent_events,
:class_name => "Event", :class_name => "Event",
:foreign_key => :author_id, :foreign_key => :author_id,
...@@ -80,7 +86,8 @@ class User < ActiveRecord::Base ...@@ -80,7 +86,8 @@ class User < ActiveRecord::Base
def self.find_for_ldap_auth(omniauth_info) def self.find_for_ldap_auth(omniauth_info)
name = omniauth_info.name.force_encoding("utf-8") 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) if @user = User.find_by_email(email)
@user @user
......
...@@ -68,7 +68,7 @@ class UsersProject < ActiveRecord::Base ...@@ -68,7 +68,7 @@ class UsersProject < ActiveRecord::Base
end end
def repo_access_human def repo_access_human
"" self.class.access_roles.invert[self.project_access]
end end
end end
# == Schema Information # == Schema Information
......
...@@ -4,8 +4,6 @@ class WebHook < ActiveRecord::Base ...@@ -4,8 +4,6 @@ class WebHook < ActiveRecord::Base
# HTTParty timeout # HTTParty timeout
default_timeout 10 default_timeout 10
belongs_to :project
validates :url, validates :url,
presence: true, presence: true,
format: { format: {
...@@ -14,9 +12,8 @@ class WebHook < ActiveRecord::Base ...@@ -14,9 +12,8 @@ class WebHook < ActiveRecord::Base
def execute(data) def execute(data)
WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) 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
end end
# == Schema Information # == Schema Information
# #
......
...@@ -43,7 +43,7 @@ class MailerObserver < ActiveRecord::Observer ...@@ -43,7 +43,7 @@ class MailerObserver < ActiveRecord::Observer
end end
def new_merge_request(merge_request) 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 Notify.new_merge_request_email(merge_request.id).deliver
end end
end end
......
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
...@@ -55,4 +55,8 @@ module Account ...@@ -55,4 +55,8 @@ module Account
# Take only latest one # Take only latest one
events = events.recent.limit(1).first events = events.recent.limit(1).first
end end
def projects_with_events
projects.includes(:events).order("events.created_at DESC")
end
end end
...@@ -27,7 +27,7 @@ module GitPush ...@@ -27,7 +27,7 @@ module GitPush
true true
end end
def execute_web_hooks(oldrev, newrev, ref, user) def execute_hooks(oldrev, newrev, ref, user)
ref_parts = ref.split('/') ref_parts = ref.split('/')
# Return if this is not a push to a branch (e.g. new commits) # Return if this is not a push to a branch (e.g. new commits)
...@@ -35,7 +35,7 @@ module GitPush ...@@ -35,7 +35,7 @@ module GitPush
data = post_receive_data(oldrev, newrev, ref, user) data = post_receive_data(oldrev, newrev, ref, user)
web_hooks.each { |web_hook| web_hook.execute(data) } hooks.each { |hook| hook.execute(data) }
end end
def post_receive_data(oldrev, newrev, ref, user) def post_receive_data(oldrev, newrev, ref, user)
...@@ -97,7 +97,7 @@ module GitPush ...@@ -97,7 +97,7 @@ module GitPush
self.update_merge_requests(oldrev, newrev, ref, user) self.update_merge_requests(oldrev, newrev, ref, user)
# Execute web hooks # Execute web hooks
self.execute_web_hooks(oldrev, newrev, ref, user) self.execute_hooks(oldrev, newrev, ref, user)
# Create satellite # Create satellite
self.satellite.create unless self.satellite.exists? self.satellite.create unless self.satellite.exists?
......
<% 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) %>
.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"
%h4 .file_holder#README
%i.icon-file .file_title
githost.log %i.icon-file
%pre.logs githost.log
- Gitlab::Logger.read_latest.each do |line| .file_content.logs
%span.log= line %ol
- Gitlab::Logger.read_latest.each do |line|
%li
%p= line
%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(); });
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
%th Team Members %th Team Members
%th Post Receive %th Post Receive
%th Last Commit %th Last Commit
%th %th Edit
%th %th.cred Danger Zone!
- @admin_projects.each do |project| - @admin_projects.each do |project|
%tr %tr
...@@ -24,5 +24,5 @@ ...@@ -24,5 +24,5 @@
%td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, :disabled => true %td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, :disabled => true
%td= last_commit(project) %td= last_commit(project)
%td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}", :class => "btn small" %td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}", :class => "btn small"
%td= 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" = paginate @admin_projects, :theme => "admin"
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
.alert .alert
.clearfix .clearfix
%p Give user ability to manage application. %p Make the user a GitLab administrator.
= f.label :admin, :class => "checkbox" do = f.label :admin, :class => "checkbox" do
= f.check_box :admin = f.check_box :admin
%span Administrator %span Administrator
...@@ -59,11 +59,11 @@ ...@@ -59,11 +59,11 @@
- if @admin_user.blocked - if @admin_user.blocked
%span %span
= link_to 'Unblock', unblock_admin_user_path(@admin_user), :method => :put, :class => "btn small" = 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 - else
%span %span
= link_to 'Block', block_admin_user_path(@admin_user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger" = 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 .actions
= f.submit 'Save', :class => "btn primary" = f.submit 'Save', :class => "btn primary"
- if @admin_user.new_record? - if @admin_user.new_record?
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
%th Projects %th Projects
%th Edit %th Edit
%th Blocked %th Blocked
%th %th.cred Danger Zone!
- @admin_users.each do |user| - @admin_users.each do |user|
%tr %tr
...@@ -41,6 +41,6 @@ ...@@ -41,6 +41,6 @@
= link_to 'Unblock', unblock_admin_user_path(user), :method => :put, :class => "btn small success" = link_to 'Unblock', unblock_admin_user_path(user), :method => :put, :class => "btn small success"
- else - else
= link_to 'Block', block_admin_user_path(user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger" = 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" = paginate @admin_users, :theme => "admin"
- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits| - @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits|
%div.ui-box %div.ui-box
%h5= day.stamp("28 Aug, 2010") %h5.small
%i.icon-calendar
= day.stamp("28 Aug, 2010")
%ul.unstyled= render commits %ul.unstyled= render commits
...@@ -35,7 +35,13 @@ ...@@ -35,7 +35,13 @@
- if file.text? - if file.text?
= render "commits/text_file", :diff => diff, :index => i = render "commits/text_file", :diff => diff, :index => i
- elsif file.image? - elsif file.image?
.diff_file_content_image{:class => image_diff_class(diff)} - if diff.renamed_file || diff.new_file || diff.deleted_file
%img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} .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 - else
%p.nothing_here_message No preview for this file type %p.nothing_here_message No preview for this file type
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
%li{:class => "#{branches_tab_class}"} %li{:class => "#{branches_tab_class}"}
= link_to project_repository_path(@project) do = link_to project_repository_path(@project) do
Branches Branches
%span.number= @project.repo.branch_count %span.badge= @project.repo.branch_count
%li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"} %li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"}
= link_to tags_project_repository_path(@project) do = link_to tags_project_repository_path(@project) do
Tags Tags
%span.number= @project.repo.tag_count %span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token - if current_page?(project_commits_path(@project)) && current_user.private_token
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
= "..." = "..."
= text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge" = text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge"
.actions .actions
= submit_tag "Compare", :class => "btn primary" = submit_tag "Compare", :class => "btn btn-primary"
- unless @commits.empty? - unless @commits.empty?
......
...@@ -8,17 +8,10 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear ...@@ -8,17 +8,10 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
@events.each do |event| @events.each do |event|
if event.allowed? if event.allowed?
event = EventDecorator.decorate(event)
xml.entry do xml.entry do
if event.issue? event_link = event.feed_url
event_link = project_issue_url(event.project, event.issue) event_title = event.feed_title
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
xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
xml.link :href => event_link xml.link :href => event_link
......
...@@ -10,9 +10,10 @@ ...@@ -10,9 +10,10 @@
add new key add new key
to your profile to your profile
- if @events.any? - if @events.any?
= render @events .content_list= render @events
- else - else
%h4.nothing_here_message Projects activity will be displayed here %h4.nothing_here_message Projects activity will be displayed here
.loading.hide
.side .side
= render "events/event_last_push", :event => @last_push = render "events/event_last_push", :event => @last_push
.projects_box .projects_box
...@@ -54,3 +55,7 @@ ...@@ -54,3 +55,7 @@
New Project » New Project »
- else - else
If you will be added to project - it will be displayed here If you will be added to project - it will be displayed here
:javascript
$(function(){ Pager.init(20); });
:plain :plain
$(".projects .activities").append("#{escape_javascript(render(@events))}"); Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
= image_tag gravatar_icon(event.author_email), :class => "avatar" = image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name} %strong #{event.author_name}
%span.event_label= event.action_name %span.event_label{:class => event.action_name}= event.action_name
&nbsp;issue issue
= link_to project_issue_path(event.project, event.issue) do = link_to project_issue_path(event.project, event.issue) do
%strong= truncate event.issue_title %strong= truncate event.issue_title
at at
......
...@@ -5,12 +5,9 @@ ...@@ -5,12 +5,9 @@
%span Your pushed to %span Your pushed to
= event.ref_type = event.ref_type
= link_to project_commits_path(event.project, :ref => event.ref_name) do = link_to project_commits_path(event.project, :ref => event.ref_name) do
%strong= event.ref_name %strong= truncate(event.ref_name, :length => 28)
at at
%strong= link_to event.project.name, event.project %strong= link_to event.project.name, event.project
%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 = link_to new_mr_path_from_push_event(event), :title => "New Merge Request", :class => "btn very_small primary" do
Create Merge Request Create Merge Request
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
.event_icon= image_tag "event_mr_merged.png" .event_icon= image_tag "event_mr_merged.png"
= image_tag gravatar_icon(event.author_email), :class => "avatar" = image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name} %strong #{event.author_name}
%span.event_label= event.action_name %span.event_label{:class => event.action_name}= event.action_name
&nbsp;merge request merge request
= link_to project_merge_request_path(event.project, event.merge_request) do = link_to project_merge_request_path(event.project, event.merge_request) do
%strong= truncate event.merge_request_title %strong= truncate event.merge_request_title
at at
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.event_icon= image_tag "event_push.png" .event_icon= image_tag "event_push.png"
= image_tag gravatar_icon(event.author_email), :class => "avatar" = image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name} %strong #{event.author_name}
%span.event_label= event.push_action_name %span.event_label.pushed= event.push_action_name
= event.ref_type = event.ref_type
= link_to project_commits_path(event.project, :ref => event.ref_name) do = link_to project_commits_path(event.project, :ref => event.ref_name) do
%strong= event.ref_name %strong= event.ref_name
......
%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"))
...@@ -22,3 +22,9 @@ ...@@ -22,3 +22,9 @@
%li %li
%h5= link_to "Web Hooks", help_web_hooks_path %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
%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"
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
.row .row
.span7= paginate @issues, :remote => true, :theme => "gitlab" .span7= paginate @issues, :remote => true, :theme => "gitlab"
.span3.right .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 - else
%li %li
%h4.nothing_here_message Nothing to show here %h4.nothing_here_message Nothing to show here
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
= issue.notes.count = issue.notes.count
- if can? current_user, :modify_issue, issue - if can? current_user, :modify_issue, issue
- if issue.closed - 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 - 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 = link_to edit_project_issue_path(issue.project, issue), :class => "btn small edit-issue-link", :remote => true do
%i.icon-edit %i.icon-edit
Edit Edit
...@@ -35,6 +35,4 @@ ...@@ -35,6 +35,4 @@
&nbsp; &nbsp;
- if issue.upvotes > 0 - if issue.upvotes > 0
%span.badge.badge-success= "+#{issue.upvotes}" %span.badge.badge-success= "+#{issue.upvotes}"
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.issues_content .issues_content
%h3.page_title %h3.page_title
Issues Issues
%small (#{@issues.total_count}) %small (<span class=issue_counter>#{@issues.total_count}</span>)
.right .right
.span5 .span5
- if can? current_user, :write_issue, @project - if can? current_user, :write_issue, @project
...@@ -45,4 +45,4 @@ ...@@ -45,4 +45,4 @@
:javascript :javascript
$(function(){ $(function(){
issuesPage(); issuesPage();
}) })
\ No newline at end of file
%h3 New key %h3.page_title New key
%hr %hr
= render 'form' = render 'form'
...@@ -11,4 +11,4 @@ ...@@ -11,4 +11,4 @@
if( key_mail && key_mail.length > 0 && title.val() == '' ){ if( key_mail && key_mail.length > 0 && title.val() == '' ){
$('#key_title').val( key_mail ); $('#key_title').val( key_mail );
} }
}); });
\ No newline at end of file
...@@ -17,14 +17,14 @@ ...@@ -17,14 +17,14 @@
%li{:class => tab_class(:issues)} %li{:class => tab_class(:issues)}
= link_to project_issues_filter_path(@project) do = link_to project_issues_filter_path(@project) do
Issues Issues
%span.count= @project.issues.opened.count %span.count.issue_counter= @project.issues.opened.count
- if @project.repo_exists? - if @project.repo_exists?
- if @project.merge_requests_enabled - if @project.merge_requests_enabled
%li{:class => tab_class(:merge_requests)} %li{:class => tab_class(:merge_requests)}
= link_to project_merge_requests_path(@project) do = link_to project_merge_requests_path(@project) do
Merge Requests Merge Requests
%span.count= @project.merge_requests.opened.count %span.count.merge_counter= @project.merge_requests.opened.count
- if @project.wall_enabled - if @project.wall_enabled
%li{:class => tab_class(:wall)} %li{:class => tab_class(:wall)}
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
%li{:class => tab_class(:admin_logs)} %li{:class => tab_class(:admin_logs)}
= link_to "Logs", admin_logs_path = link_to "Logs", admin_logs_path
%li{:class => tab_class(:admin_emails)} %li{:class => tab_class(:admin_emails)}
= link_to "Emails", admin_emails_path = link_to "Hooks", admin_hooks_path
%li{:class => tab_class(:admin_resque)} %li{:class => tab_class(:admin_resque)}
= link_to "Resque", admin_resque_path = link_to "Resque", admin_resque_path
......
...@@ -12,16 +12,17 @@ ...@@ -12,16 +12,17 @@
%li{:class => tab_class(:password)} %li{:class => tab_class(:password)}
= link_to "Password", profile_password_path = 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)} %li{:class => tab_class(:token)}
= link_to "Token", profile_token_path = link_to "Token", profile_token_path
%li{:class => tab_class(:design)} %li{:class => tab_class(:design)}
= link_to "Design", profile_design_path = 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 .content
= yield = yield
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
- @merge_request.errors.full_messages.each do |msg| - @merge_request.errors.full_messages.each do |msg|
%li= msg %li= msg
%h3.padded.cgray 1. Select Branches %h4.cdark 1. Select Branches
%br
.row .row
.span6 .span6
...@@ -30,14 +31,21 @@ ...@@ -30,14 +31,21 @@
.bottom_commit .bottom_commit
.mr_target_commit .mr_target_commit
%h3.padded.cgray 2. Fill info %h4.cdark 2. Fill info
.clearfix .clearfix
= f.label :assignee_id, "Assign to", :class => "control-label" .main_box
.controls= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px") .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 .control-group
= f.label :title, :class => "control-label"
.controls= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5
.form-actions .form-actions
= f.submit 'Save', :class => "btn-primary btn" = f.submit 'Save', :class => "btn-primary btn"
......
...@@ -15,12 +15,14 @@ ...@@ -15,12 +15,14 @@
&rarr; &rarr;
= merge_request.target_branch = merge_request.target_branch
= image_tag gravatar_icon(merge_request.author_email), :class => "avatar" = 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 %span.update-author
%strong= merge_request.author_name %small.cdark= "##{merge_request.id}"
authored authored by #{merge_request.author_name}
= time_ago_in_words(merge_request.created_at) = time_ago_in_words(merge_request.created_at)
ago ago
- if merge_request.upvotes > 0 - if merge_request.upvotes > 0
%span.badge.badge-success= "+#{merge_request.upvotes}" %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)
%h3 %h3.page_title
= "Edit merge request #{@merge_request.id}" = "Edit merge request #{@merge_request.id}"
%hr %hr
= render 'form' = render 'form'
%h3 New Merge Request %h3.page_title New Merge Request
%hr %hr
= render 'form' = render 'form'
- if @commits.present? - if @commits.present?
.ui-box .ui-box
%h5 Commits (#{@commits.count}) %h5
%i.icon-list
Commits (#{@commits.count})
.merge-request-commits .merge-request-commits
- if @commits.count > 8 - if @commits.count > 8
%ul.first_mr_commits.unstyled %ul.first_mr_commits.unstyled
......
...@@ -13,9 +13,10 @@ ...@@ -13,9 +13,10 @@
= image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av" = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_author(@merge_request) %strong.author= link_to_merge_request_author(@merge_request)
%cite.cgray and currently assigned to - if @merge_request.assignee
= image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av" %cite.cgray and currently assigned to
%strong.author= link_to_merge_request_assignee(@merge_request) = 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 - if @merge_request.closed
......
...@@ -32,4 +32,4 @@ ...@@ -32,4 +32,4 @@
%span Any file less than 10 MB %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"
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= check_box_tag :notify_author, 1 , @note.noteable_type == "Commit" = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
%span Commit author %span Commit author
.actions .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" = link_to "Close", "#", :class => "btn hide-button"
:javascript :javascript
......
%tr.line_notes_row.reply %tr.line_notes_row.reply
%td{:colspan => 3} %td{:colspan => 3}
%i.icon-comment
= link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line" = link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line"
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree } = render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree }
- else - else
- contents = tree.contents - contents = tree.contents
%table#tree-slider.bordered-table.table %table#tree-slider.bordered-table.table{:class => "table_#{@hex_path}" }
%thead %thead
%th Name %th Name
%th Last Update %th Last Update
...@@ -29,34 +29,39 @@ ...@@ -29,34 +29,39 @@
%td %td
%td %td
- index = 0
- contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content| - 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| - 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| - 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 - if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first
#tree-readme-holder .file_holder#README
%h3= content.name .file_title
.readme %i.icon-file
= content.name
.file_content.wiki
- if content.name =~ /\.(md|markdown)$/i - if content.name =~ /\.(md|markdown)$/i
= preserve do = preserve do
= markdown(content.data) = markdown(content.data)
- else - else
= simple_format(content.data) = 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 :javascript
$(function(){ $(function(){
$('select#branch').selectmenu({style:'popup', width:200}); $('select#branch').selectmenu({style:'popup', width:200});
$('select#tag').selectmenu({style:'popup', width:200}); $('select#tag').selectmenu({style:'popup', width:200});
$('.project-refs-select').chosen(); $('.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}');
}); });
......
- 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 .file_holder
.view_file_header .file_title
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= name = name
...@@ -10,26 +10,28 @@ ...@@ -10,26 +10,28 @@
= link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small" = link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
- if file.text? - if file.text?
- if name =~ /\.(md|markdown)$/i - if name =~ /\.(md|markdown)$/i
#tree-readme-holder .file_content.wiki
.readme = preserve do
= preserve do = markdown(file.data)
= markdown(file.data)
- else - else
.view_file_content .file_content.code
- unless file.empty? - unless file.empty?
%div{:class => current_user.dark_scheme ? "black" : "white"} %div{:class => current_user.dark_scheme ? "black" : "white"}
= preserve do = preserve do
= raw file.colorize(options: { linenos: 'True'}) = raw file.colorize(options: { linenos: 'True'})
- else - else
%h4.nothing_here_message Empty file %h4.nothing_here_message Empty file
- elsif file.image? - elsif file.image?
.view_file_content_image .file_content.image_file
%img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else - else
%center .file_content.blob_file
= link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do %center
%div.padded = link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do
%br %div.padded
= image_tag "download.png", :width => 64 %br
%h3 = image_tag "download.png", :width => 64
Download (#{file.mb_size}) %h3
Download (#{file.mb_size})
- file = params[:path] ? File.join(params[:path], content.name) : content.name - file = tree_full_path(content)
- content_commit = @project.commits(@commit.id, file, 1).last %tr{ :class => "tree-item #{tree_hex_class(content)}", :url => tree_file_project_ref_path(@project, @ref, file) }
- return unless content_commit
%tr{ :class => "tree-item", :url => tree_file_project_ref_path(@project, @ref, file) }
%td.tree-item-file-name %td.tree-item-file-name
- if content.is_a?(Grit::Blob) = tree_icon(content)
- 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"
= link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true
%td.cgray %td.tree_time_ago.cgray
= time_ago_in_words(content_commit.committed_date) - if index == 1
ago %span.log_loading
%td.commit Loading commit data..
- tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name) = image_tag "ajax_loader_tree.gif", :width => 14
- if tm %td.tree_commit
%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"
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
%li= link %li= link
.clear .clear
.view_file.blame_file .file_holder
.view_file_header .file_title
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= @tree.name = @tree.name
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
= link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank" = 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 "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" = link_to "source", tree_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
.view_file_content .file_content.blame
%table %table
- @blame.each do |commit, lines| - @blame.each do |commit, lines|
- commit = Commit.new(commit) - commit = Commit.new(commit)
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
%td.author %td.author
= image_tag gravatar_icon(commit.author_email, 16) = image_tag gravatar_icon(commit.author_email, 16)
= commit.author_name = commit.author_name
%td.commit %td.blame_commit
&nbsp; &nbsp;
= link_to project_commit_path(@project, :id => commit.id) do = link_to project_commit_path(@project, :id => commit.id) do
%code= commit.id.to_s[0..10] %code= commit.id.to_s[0..10]
...@@ -37,8 +37,7 @@ ...@@ -37,8 +37,7 @@
%td.lines %td.lines
= preserve do = preserve do
%pre %pre
- lines.each do |line| = Gitlab::Encode.utf8 lines.join("\n")
= line
:javascript :javascript
$(function(){ $(function(){
......
- @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))}');
:plain :plain
// Load Files list
$("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}"); $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}");
$("#tree-content-holder").show("slide", { direction: "right" }, 150); $("#tree-content-holder").show("slide", { direction: "right" }, 150);
$('.project-refs-form #path').val("#{params[:path]}"); $('.project-refs-form #path').val("#{params[:path]}");
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}');
});
...@@ -7,16 +7,14 @@ ...@@ -7,16 +7,14 @@
= link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right" = link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right"
%br %br
#tree-holder .file_holder
#tree-content-holder .file_title
.view_file %i.icon-file
.view_file_header %strong= @snippet.file_name
%i.icon-file %span.options
%strong= @snippet.file_name = link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"
%span.options .file_content.code
= link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank" %div{:class => current_user.dark_scheme ? "black" : ""}
.view_file_content = raw @snippet.colorize(options: { linenos: 'True'})
%div{:class => current_user.dark_scheme ? "black" : ""}
= raw @snippet.colorize(options: { linenos: 'True'})
= render "notes/notes", :tid => @snippet.id, :tt => "snippet" = render "notes/notes", :tid => @snippet.id, :tt => "snippet"
class SystemHookWorker
@queue = :system_hook
def self.perform(hook_id, data)
SystemHook.find(hook_id).execute data
end
end
...@@ -23,7 +23,7 @@ module Gitlab ...@@ -23,7 +23,7 @@ module Gitlab
# config.plugins = [ :exception_notification, :ssl_requirement, :all ] # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running. # 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. # 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. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
......
...@@ -21,6 +21,8 @@ email: ...@@ -21,6 +21,8 @@ email:
# Like default project limit for user etc # Like default project limit for user etc
app: app:
default_projects_limit: 10 default_projects_limit: 10
# backup_path: "/vol/backups" # default: Rails.root + backups/
# backup_keep_time: 604800 # default: 0 (forever) (in seconds)
# #
......
...@@ -95,11 +95,21 @@ class Settings < Settingslogic ...@@ -95,11 +95,21 @@ class Settings < Settingslogic
end end
def gitolite_admin_uri def gitolite_admin_uri
git['admin_uri'] || 'git@localhost:gitolite-admin' git_host['admin_uri'] || 'git@localhost:gitolite-admin'
end end
def default_projects_limit def default_projects_limit
app['default_projects_limit'] || 10 app['default_projects_limit'] || 10
end 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
end end
...@@ -93,10 +93,6 @@ Devise.setup do |config| ...@@ -93,10 +93,6 @@ Devise.setup do |config|
# If true, extends the user's remember period when remembered via cookie. # If true, extends the user's remember period when remembered via cookie.
# config.extend_remember_period = false # config.extend_remember_period = false
# If true, uses the password salt as remember token. This should be turned
# to false if you are not using database authenticatable.
config.use_salt_as_remember_token = true
# Options to be passed to the created cookie. For instance, you can set # Options to be passed to the created cookie. For instance, you can set
# :secure => true in order to force SSL only cookies. # :secure => true in order to force SSL only cookies.
# config.cookie_options = {} # config.cookie_options = {}
...@@ -119,7 +115,7 @@ Devise.setup do |config| ...@@ -119,7 +115,7 @@ Devise.setup do |config|
# Defines which strategy will be used to lock an account. # Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in. # :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :none = No lock strategy. You should handle locking by yourself. # :none = No lock strategy. You should handle locking by yourself.
# config.lock_strategy = :failed_attempts config.lock_strategy = :failed_attempts
# Defines which key will be used when locking and unlocking an account # Defines which key will be used when locking and unlocking an account
# config.unlock_keys = [ :email ] # config.unlock_keys = [ :email ]
...@@ -129,14 +125,14 @@ Devise.setup do |config| ...@@ -129,14 +125,14 @@ Devise.setup do |config|
# :time = Re-enables login after a certain amount of time (see :unlock_in below) # :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies # :both = Enables both strategies
# :none = No unlock strategy. You should handle unlocking by yourself. # :none = No unlock strategy. You should handle unlocking by yourself.
# config.unlock_strategy = :both config.unlock_strategy = :time
# Number of authentication tries before locking an account if lock_strategy # Number of authentication tries before locking an account if lock_strategy
# is failed attempts. # is failed attempts.
# config.maximum_attempts = 20 config.maximum_attempts = 10
# Time interval to unlock the account if :time is enabled as unlock_strategy. # Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour config.unlock_in = 10.minutes
# ==> Configuration for :recoverable # ==> Configuration for :recoverable
# #
...@@ -160,9 +156,9 @@ Devise.setup do |config| ...@@ -160,9 +156,9 @@ Devise.setup do |config|
# Defines name of the authentication token params key # Defines name of the authentication token params key
config.token_authentication_key = :private_token config.token_authentication_key = :private_token
# If true, authentication through token does not store user in session and needs # Authentication through token does not store user in session and needs
# to be supplied on each request. Useful if you are using the token as API token. # to be supplied on each request. Useful if you are using the token as API token.
config.stateless_token = true config.skip_session_storage << :token_auth
# ==> Scopes configuration # ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for # Turn scoped views on. Before rendering "sessions/new", it will first check for
......
...@@ -35,13 +35,11 @@ en: ...@@ -35,13 +35,11 @@ en:
confirmed: 'Your account was successfully confirmed. You are now signed in.' confirmed: 'Your account was successfully confirmed. You are now signed in.'
registrations: registrations:
signed_up: 'Welcome! You have signed up successfully.' signed_up: 'Welcome! You have signed up successfully.'
inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.'
updated: 'You updated your account successfully.' updated: 'You updated your account successfully.'
destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
reasons: signed_up_but_unconfirmed: 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.'
inactive: 'inactive' signed_up_but_inactive: 'You have signed up successfully. However, we could not sign you in because your account is not yet activated.'
unconfirmed: 'unconfirmed' signed_up_but_locked: 'You have signed up successfully. However, we could not sign you in because your account is locked.'
locked: 'locked'
unlocks: unlocks:
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
unlocked: 'Your account was successfully unlocked. You are now signed in.' unlocked: 'Your account was successfully unlocked. You are now signed in.'
......
...@@ -26,7 +26,9 @@ Gitlab::Application.routes.draw do ...@@ -26,7 +26,9 @@ Gitlab::Application.routes.draw do
get 'help' => 'help#index' get 'help' => 'help#index'
get 'help/permissions' => 'help#permissions' get 'help/permissions' => 'help#permissions'
get 'help/workflow' => 'help#workflow' get 'help/workflow' => 'help#workflow'
get 'help/api' => 'help#api'
get 'help/web_hooks' => 'help#web_hooks' get 'help/web_hooks' => 'help#web_hooks'
get 'help/system_hooks' => 'help#system_hooks'
# #
# Admin Area # Admin Area
...@@ -46,11 +48,13 @@ Gitlab::Application.routes.draw do ...@@ -46,11 +48,13 @@ Gitlab::Application.routes.draw do
end end
end end
resources :team_members, :only => [:edit, :update, :destroy] resources :team_members, :only => [:edit, :update, :destroy]
get 'emails', :to => 'mailer#preview'
get 'mailer/preview_note' get 'mailer/preview_note'
get 'mailer/preview_user_new' get 'mailer/preview_user_new'
get 'mailer/preview_issue_new' get 'mailer/preview_issue_new'
resources :hooks, :only => [:index, :create, :destroy] do
get :test
end
resource :logs resource :logs
resource :resque, :controller => 'resque' resource :resque, :controller => 'resque'
root :to => "dashboard#index" root :to => "dashboard#index"
...@@ -116,6 +120,8 @@ Gitlab::Application.routes.draw do ...@@ -116,6 +120,8 @@ Gitlab::Application.routes.draw do
member do member do
get "tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ } get "tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ }
get "logs_tree", :constraints => { :id => /[a-zA-Z.\/0-9_\-]+/ }
get "blob", get "blob",
:constraints => { :constraints => {
:id => /[a-zA-Z.0-9\/_\-]+/, :id => /[a-zA-Z.0-9\/_\-]+/,
...@@ -131,6 +137,14 @@ Gitlab::Application.routes.draw do ...@@ -131,6 +137,14 @@ Gitlab::Application.routes.draw do
:path => /.*/ :path => /.*/
} }
# tree viewer
get "logs_tree/:path" => "refs#logs_tree",
:as => :logs_file,
:constraints => {
:id => /[a-zA-Z.0-9\/_\-]+/,
:path => /.*/
}
# blame # blame
get "blame/:path" => "refs#blame", get "blame/:path" => "refs#blame",
:as => :blame_file, :as => :blame_file,
......
class DeviseCreateUsers < ActiveRecord::Migration class DeviseCreateUsers < ActiveRecord::Migration
def self.up def self.up
create_table(:users) do |t| create_table(:users) do |t|
t.database_authenticatable :null => false ## Database authenticatable
t.recoverable t.string :email, :null => false, :default => ""
t.rememberable t.string :encrypted_password, :null => false, :default => ""
t.trackable
## Recoverable
# t.encryptable t.string :reset_password_token
# t.confirmable t.datetime :reset_password_sent_at
# t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
# t.token_authenticatable ## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Encryptable
# t.string :password_salt
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
# Token authenticatable
# t.string :authentication_token
## Invitable
# t.string :invitation_token
t.timestamps t.timestamps
end end
......
class AddLockableToUsers < ActiveRecord::Migration
def change
add_column :users, :failed_attempts, :integer, :default => 0
add_column :users, :locked_at, :datetime
end
end
class AddTypeToWebHook < ActiveRecord::Migration
def change
add_column :web_hooks, :type, :string, :default => "ProjectHook"
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20120627145613) do ActiveRecord::Schema.define(:version => 20120712080407) do
create_table "events", :force => true do |t| create_table "events", :force => true do |t|
t.string "target_type" t.string "target_type"
...@@ -169,6 +169,8 @@ ActiveRecord::Schema.define(:version => 20120627145613) do ...@@ -169,6 +169,8 @@ ActiveRecord::Schema.define(:version => 20120627145613) do
t.integer "theme_id", :default => 1, :null => false t.integer "theme_id", :default => 1, :null => false
t.string "bio" t.string "bio"
t.boolean "blocked", :default => false, :null => false t.boolean "blocked", :default => false, :null => false
t.integer "failed_attempts", :default => 0
t.datetime "locked_at"
end end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true add_index "users", ["email"], :name => "index_users_on_email", :unique => true
...@@ -185,8 +187,9 @@ ActiveRecord::Schema.define(:version => 20120627145613) do ...@@ -185,8 +187,9 @@ ActiveRecord::Schema.define(:version => 20120627145613) do
create_table "web_hooks", :force => true do |t| create_table "web_hooks", :force => true do |t|
t.string "url" t.string "url"
t.integer "project_id" t.integer "project_id"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.string "type", :default => "ProjectHook"
end end
create_table "wikis", :force => true do |t| create_table "wikis", :force => true do |t|
......
...@@ -60,7 +60,7 @@ Also read the [Read this before you submit an issue](https://github.com/gitlabhq ...@@ -60,7 +60,7 @@ Also read the [Read this before you submit an issue](https://github.com/gitlabhq
sudo apt-get update sudo apt-get update
sudo apt-get upgrade sudo apt-get upgrade
sudo apt-get install -y wget curl gcc checkinstall libxml2-dev libxslt-dev sqlite3 libsqlite3-dev libcurl4-openssl-dev libreadline-gplv2-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev libicu-dev redis-server openssh-server git-core python-dev python-pip libyaml-dev sendmail sudo apt-get install -y wget curl gcc checkinstall libxml2-dev libxslt-dev sqlite3 libsqlite3-dev libcurl4-openssl-dev libreadline6-dev libc6-dev libssl-dev libmysql++-dev make build-essential zlib1g-dev libicu-dev redis-server openssh-server git-core python-dev python-pip libyaml-dev sendmail
# If you want to use MySQL: # If you want to use MySQL:
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
...@@ -107,10 +107,10 @@ Get gitolite source code: ...@@ -107,10 +107,10 @@ Get gitolite source code:
Setup: Setup:
sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" > /home/git/.profile' sudo -u git sh -c 'echo -e "PATH=\$PATH:/home/git/bin\nexport PATH" >> /home/git/.profile'
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install" sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; /home/git/gitolite/src/gl-system-install"
sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub sudo cp /home/gitlab/.ssh/id_rsa.pub /home/git/gitlab.pub
sudo chmod 777 /home/git/gitlab.pub sudo chmod 0444 /home/git/gitlab.pub
sudo -u git -H sed -i 's/0077/0007/g' /home/git/share/gitolite/conf/example.gitolite.rc sudo -u git -H sed -i 's/0077/0007/g' /home/git/share/gitolite/conf/example.gitolite.rc
sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gl-setup -q /home/git/gitlab.pub" sudo -u git -H sh -c "PATH=/home/git/bin:$PATH; gl-setup -q /home/git/gitlab.pub"
...@@ -139,6 +139,8 @@ Permissions: ...@@ -139,6 +139,8 @@ Permissions:
cd /home/gitlab cd /home/gitlab
sudo -H -u gitlab git clone -b stable git://github.com/gitlabhq/gitlabhq.git gitlab sudo -H -u gitlab git clone -b stable git://github.com/gitlabhq/gitlabhq.git gitlab
cd gitlab cd gitlab
sudo -u gitlab mkdir tmp
# Rename config files # Rename config files
sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml sudo -u gitlab cp config/gitlab.yml.example config/gitlab.yml
...@@ -216,15 +218,15 @@ Application can be started with next command: ...@@ -216,15 +218,15 @@ Application can be started with next command:
sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb
sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D
Edit /etc/nginx/nginx.conf. Add in **http** section: Edit /etc/nginx/nginx.conf. In the *http* section add:
upstream gitlab { upstream gitlab {
server unix:/home/gitlab/gitlab/tmp/sockets/gitlab.socket; server unix:/home/gitlab/gitlab/tmp/sockets/gitlab.socket;
} }
server { server {
listen YOUR_SERVER_IP:80; listen YOUR_SERVER_IP:80; # e.g., listen 192.168.1.1:80;
server_name gitlab.YOUR_DOMAIN.com; server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com;
root /home/gitlab/gitlab/public; root /home/gitlab/gitlab/public;
# individual nginx logs for this gitlab vhost # individual nginx logs for this gitlab vhost
...@@ -232,26 +234,26 @@ Edit /etc/nginx/nginx.conf. Add in **http** section: ...@@ -232,26 +234,26 @@ Edit /etc/nginx/nginx.conf. Add in **http** section:
error_log /var/log/nginx/gitlab_error.log; error_log /var/log/nginx/gitlab_error.log;
location / { location / {
# serve static files from defined root folder;. # serve static files from defined root folder;.
# @gitlab is a named location for the upstream fallback, see below # @gitlab is a named location for the upstream fallback, see below
try_files $uri $uri/index.html $uri.html @gitlab; try_files $uri $uri/index.html $uri.html @gitlab;
} }
# if a file, which is not found in the root folder is requested, # if a file, which is not found in the root folder is requested,
# then the proxy pass the request to the upsteam (gitlab unicorn) # then the proxy pass the request to the upsteam (gitlab unicorn)
location @gitlab { location @gitlab {
proxy_redirect off; proxy_redirect off;
# you need to change this to "https", if you set "ssl" directive to "on" # you need to change this to "https", if you set "ssl" directive to "on"
proxy_set_header X-FORWARDED_PROTO http; proxy_set_header X-FORWARDED_PROTO http;
proxy_set_header Host gitlab.YOUR_SUBDOMAIN.com:80; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://gitlab; proxy_pass http://gitlab;
} }
} }
gitlab.YOUR_DOMAIN.com - change to your domain. Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** to the IP address and fully-qualified domain name of the host serving GitLab.
Restart nginx: Restart nginx:
......
module Gitlab module Gitlab
class Logger class Logger < ::Logger
def self.error(message) def self.error(message)
@@logger ||= ::Logger.new(File.join(Rails.root, "log/githost.log")) build.error(message)
message = Time.now.to_s(:long) + " -> " + message end
@@logger.error(message)
def self.info(message)
build.info(message)
end end
def self.read_latest def self.read_latest
path = Rails.root.join("log/githost.log") path = Rails.root.join("log/githost.log")
logs = `tail -n 50 #{path}`.split("\n") logs = File.read(path).split("\n")
end end
def self.build
new(File.join(Rails.root, "log/githost.log"))
end
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_s(:long)} -> #{severity} -> #{msg}\n"
end
end end
end end
require 'active_record/fixtures'
namespace :gitlab do
namespace :app do
# Create backup of gitlab system
desc "GITLAB | Create a backup of the gitlab system"
task :backup_create => :environment do
Rake::Task["gitlab:app:db_dump"].invoke
Rake::Task["gitlab:app:repo_dump"].invoke
Dir.chdir(Gitlab.config.backup_path)
# saving additional informations
s = Hash.new
s["db_version"] = "#{ActiveRecord::Migrator.current_version}"
s["backup_created_at"] = "#{Time.now}"
s["gitlab_version"] = %x{git rev-parse HEAD}.gsub(/\n/,"")
s["tar_version"] = %x{tar --version | head -1}.gsub(/\n/,"")
File.open("#{Gitlab.config.backup_path}/backup_information.yml", "w+") do |file|
file << s.to_yaml.gsub(/^---\n/,'')
end
# create archive
print "Creating backup archive: #{Time.now.to_i}_gitlab_backup.tar "
if Kernel.system("tar -cf #{Time.now.to_i}_gitlab_backup.tar repositories/ db/ backup_information.yml")
puts "[DONE]".green
else
puts "[FAILED]".red
end
# cleanup: remove tmp files
print "Deletion of tmp directories..."
if Kernel.system("rm -rf repositories/ db/ backup_information.yml")
puts "[DONE]".green
else
puts "[FAILED]".red
end
# delete backups
print "Deleting old backups... "
if Gitlab.config.backup_keep_time > 0
file_list = Dir.glob("*_gitlab_backup.tar").map { |f| f.split(/_/).first.to_i }
file_list.sort.each do |timestamp|
if Time.at(timestamp) < (Time.now - Gitlab.config.backup_keep_time)
%x{rm #{timestamp}_gitlab_backup.tar}
end
end
puts "[DONE]".green
else
puts "[SKIPPING]".yellow
end
end
# Restore backup of gitlab system
desc "GITLAB | Restore a previously created backup"
task :backup_restore => :environment do
Dir.chdir(Gitlab.config.backup_path)
# check for existing backups in the backup dir
file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i }
puts "no backup found" if file_list.count == 0
if file_list.count > 1 && ENV["BACKUP"].nil?
puts "Found more than one backup, please specify which one you want to restore:"
puts "rake gitlab:app:backup_restore BACKUP=timestamp_of_backup"
exit 1;
end
tar_file = ENV["BACKUP"].nil? ? File.join(file_list.first.to_s + "_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar")
unless File.exists?(tar_file)
puts "The specified backup doesn't exist!"
exit 1;
end
print "Unpacking backup... "
unless Kernel.system("tar -xf #{tar_file}")
puts "[FAILED]".red
exit 1
else
puts "[DONE]".green
end
settings = YAML.load_file("backup_information.yml")
ENV["VERSION"] = "#{settings["db_version"]}" if settings["db_version"].to_i > 0
# restoring mismatching backups can lead to unexpected problems
if settings["gitlab_version"] != %x{git rev-parse HEAD}.gsub(/\n/,"")
puts "gitlab_version mismatch:".red
puts " Your current HEAD differs from the HEAD in the backup!".red
puts " Please switch to the following revision and try again:".red
puts " revision: #{settings["gitlab_version"]}".red
exit 1
end
Rake::Task["gitlab:app:db_restore"].invoke
Rake::Task["gitlab:app:repo_restore"].invoke
# cleanup: remove tmp files
print "Deletion of tmp directories..."
if Kernel.system("rm -rf repositories/ db/ backup_information.yml")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
################################################################################
################################# invoked tasks ################################
################################# REPOSITORIES #################################
task :repo_dump => :environment do
backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
FileUtils.mkdir_p(backup_path_repo) until Dir.exists?(backup_path_repo)
puts "Dumping repositories:"
project = Project.all.map { |n| [n.name,n.path_to_repo] }
project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
project.each do |project|
print "- Dumping repository #{project.first}... "
if Kernel.system("cd #{project.second} > /dev/null 2>&1 && git bundle create #{backup_path_repo}/#{project.first}.bundle --all > /dev/null 2>&1")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
end
task :repo_restore => :environment do
backup_path_repo = File.join(Gitlab.config.backup_path, "repositories")
puts "Restoring repositories:"
project = Project.all.map { |n| [n.name,n.path_to_repo] }
project << ["gitolite-admin.git", File.join(File.dirname(project.first.second), "gitolite-admin.git")]
project.each do |project|
print "- Restoring repository #{project.first}... "
FileUtils.rm_rf(project.second) if File.dirname(project.second) # delet old stuff
if Kernel.system("cd #{File.dirname(project.second)} > /dev/null 2>&1 && git clone --bare #{backup_path_repo}/#{project.first}.bundle #{project.first}.git > /dev/null 2>&1")
puts "[DONE]".green
else
puts "[FAILED]".red
end
end
end
###################################### DB ######################################
task :db_dump => :environment do
backup_path_db = File.join(Gitlab.config.backup_path, "db")
FileUtils.mkdir_p(backup_path_db) until Dir.exists?(backup_path_db)
puts "Dumping database tables:"
ActiveRecord::Base.connection.tables.each do |tbl|
print "- Dumping table #{tbl}... "
count = 1
File.open(File.join(backup_path_db, tbl + ".yml"), "w+") do |file|
ActiveRecord::Base.connection.select_all("SELECT * FROM `#{tbl}`").each do |line|
line.delete_if{|k,v| v.blank?}
output = {tbl + '_' + count.to_s => line}
file << output.to_yaml.gsub(/^---\n/,'') + "\n"
count += 1
end
puts "[DONE]".green
end
end
end
task :db_restore=> :environment do
backup_path_db = File.join(Gitlab.config.backup_path, "db")
puts "Restoring database tables:"
Rake::Task["db:reset"].invoke
Dir.glob(File.join(backup_path_db, "*.yml") ).each do |dir|
fixture_file = File.basename(dir, ".*" )
print "- Loading fixture #{fixture_file}..."
if File.size(dir) > 0
ActiveRecord::Fixtures.create_fixtures(backup_path_db, fixture_file)
puts "[DONE]".green
else
puts "[SKIPPING]".yellow
end
end
end
end # namespace end: app
end # namespace end: gitlab
mkdir -p tmp/pids mkdir -p tmp/pids
bundle exec rake environment resque:work QUEUE=post_receive,mailer RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid BACKGROUND=yes bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid BACKGROUND=yes
bundle exec rake environment resque:work QUEUE=* VVERBOSE=1 bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook VVERBOSE=1
...@@ -78,7 +78,7 @@ describe Gitlab::API do ...@@ -78,7 +78,7 @@ describe Gitlab::API do
end end
describe "DELETE /projects/:id/snippets/:snippet_id" do describe "DELETE /projects/:id/snippets/:snippet_id" do
it "should create a new project snippet" do it "should delete existing project snippet" do
expect { expect {
delete "#{api_prefix}/projects/#{project.code}/snippets/#{snippet.id}?private_token=#{user.private_token}" delete "#{api_prefix}/projects/#{project.code}/snippets/#{snippet.id}?private_token=#{user.private_token}"
}.should change { Snippet.count }.by(-1) }.should change { Snippet.count }.by(-1)
......
...@@ -7,6 +7,12 @@ Factory.add(:project, Project) do |obj| ...@@ -7,6 +7,12 @@ Factory.add(:project, Project) do |obj|
obj.code = 'LGT' obj.code = 'LGT'
end end
Factory.add(:project_without_owner, Project) do |obj|
obj.name = Faker::Internet.user_name
obj.path = 'gitlabhq'
obj.code = 'LGT'
end
Factory.add(:public_project, Project) do |obj| Factory.add(:public_project, Project) do |obj|
obj.name = Faker::Internet.user_name obj.name = Faker::Internet.user_name
obj.path = 'gitlabhq' obj.path = 'gitlabhq'
...@@ -60,7 +66,11 @@ Factory.add(:key, Key) do |obj| ...@@ -60,7 +66,11 @@ Factory.add(:key, Key) do |obj|
obj.key = File.read(File.join(Rails.root, "db", "pkey.example")) obj.key = File.read(File.join(Rails.root, "db", "pkey.example"))
end end
Factory.add(:web_hook, WebHook) do |obj| Factory.add(:project_hook, ProjectHook) do |obj|
obj.url = Faker::Internet.uri("http")
end
Factory.add(:system_hook, SystemHook) do |obj|
obj.url = Faker::Internet.uri("http") obj.url = Faker::Internet.uri("http")
end end
......
...@@ -13,7 +13,6 @@ describe MergeRequest do ...@@ -13,7 +13,6 @@ describe MergeRequest do
it { should validate_presence_of(:title) } it { should validate_presence_of(:title) }
it { should validate_presence_of(:author_id) } it { should validate_presence_of(:author_id) }
it { should validate_presence_of(:project_id) } it { should validate_presence_of(:project_id) }
it { should validate_presence_of(:assignee_id) }
end end
describe "Scope" do describe "Scope" do
......
...@@ -21,44 +21,44 @@ describe Project, "Hooks" do ...@@ -21,44 +21,44 @@ describe Project, "Hooks" do
end end
end end
describe "Web hooks" do describe "Project hooks" do
context "with no web hooks" do context "with no web hooks" do
it "raises no errors" do it "raises no errors" do
lambda { lambda {
project.execute_web_hooks('oldrev', 'newrev', 'ref', @user) project.execute_hooks('oldrev', 'newrev', 'ref', @user)
}.should_not raise_error }.should_not raise_error
end end
end end
context "with web hooks" do context "with web hooks" do
before do before do
@webhook = Factory(:web_hook) @project_hook = Factory(:project_hook)
@webhook_2 = Factory(:web_hook) @project_hook_2 = Factory(:project_hook)
project.web_hooks << [@webhook, @webhook_2] project.hooks << [@project_hook, @project_hook_2]
end end
it "executes multiple web hook" do it "executes multiple web hook" do
@webhook.should_receive(:execute).once @project_hook.should_receive(:execute).once
@webhook_2.should_receive(:execute).once @project_hook_2.should_receive(:execute).once
project.execute_web_hooks('oldrev', 'newrev', 'refs/heads/master', @user) project.execute_hooks('oldrev', 'newrev', 'refs/heads/master', @user)
end end
end end
context "does not execute web hooks" do context "does not execute web hooks" do
before do before do
@webhook = Factory(:web_hook) @project_hook = Factory(:project_hook)
project.web_hooks << [@webhook] project.hooks << [@project_hook]
end end
it "when pushing a branch for the first time" do it "when pushing a branch for the first time" do
@webhook.should_not_receive(:execute) @project_hook.should_not_receive(:execute)
project.execute_web_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user) project.execute_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user)
end end
it "when pushing tags" do it "when pushing tags" do
@webhook.should_not_receive(:execute) @project_hook.should_not_receive(:execute)
project.execute_web_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0', @user) project.execute_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0', @user)
end end
end end
......
...@@ -11,7 +11,7 @@ describe Project do ...@@ -11,7 +11,7 @@ describe Project do
it { should have_many(:issues).dependent(:destroy) } it { should have_many(:issues).dependent(:destroy) }
it { should have_many(:notes).dependent(:destroy) } it { should have_many(:notes).dependent(:destroy) }
it { should have_many(:snippets).dependent(:destroy) } it { should have_many(:snippets).dependent(:destroy) }
it { should have_many(:web_hooks).dependent(:destroy) } it { should have_many(:hooks).dependent(:destroy) }
it { should have_many(:deploy_keys).dependent(:destroy) } it { should have_many(:deploy_keys).dependent(:destroy) }
end end
......
require "spec_helper"
describe SystemHook do
describe "execute" do
before(:each) { ActiveRecord::Base.observers.enable(:all) }
before(:each) do
@system_hook = Factory :system_hook
WebMock.stub_request(:post, @system_hook.url)
end
it "project_create hook" do
user = Factory :user
with_resque do
project = Factory :project_without_owner, :owner => user
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /project_create/).once
end
it "project_destroy hook" do
project = Factory :project
with_resque do
project.destroy
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /project_destroy/).once
end
it "user_create hook" do
with_resque do
Factory :user
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_create/).once
end
it "user_destroy hook" do
user = Factory :user
with_resque do
user.destroy
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_destroy/).once
end
it "project_create hook" do
user = Factory :user
project = Factory :project
with_resque do
project.users << user
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_add_to_team/).once
end
it "project_destroy hook" do
user = Factory :user
project = Factory :project
project.users << user
with_resque do
project.users_projects.clear
end
WebMock.should have_requested(:post, @system_hook.url).with(body: /user_remove_from_team/).once
end
end
end
require 'spec_helper' require 'spec_helper'
describe WebHook do describe ProjectHook do
describe "Associations" do describe "Associations" do
it { should belong_to :project } it { should belong_to :project }
end end
...@@ -23,32 +23,32 @@ describe WebHook do ...@@ -23,32 +23,32 @@ describe WebHook do
describe "execute" do describe "execute" do
before(:each) do before(:each) do
@webhook = Factory :web_hook @project_hook = Factory :project_hook
@project = Factory :project @project = Factory :project
@project.web_hooks << [@webhook] @project.hooks << [@project_hook]
@data = { before: 'oldrev', after: 'newrev', ref: 'ref'} @data = { before: 'oldrev', after: 'newrev', ref: 'ref'}
WebMock.stub_request(:post, @webhook.url) WebMock.stub_request(:post, @project_hook.url)
end end
it "POSTs to the web hook URL" do it "POSTs to the web hook URL" do
@webhook.execute(@data) @project_hook.execute(@data)
WebMock.should have_requested(:post, @webhook.url).once WebMock.should have_requested(:post, @project_hook.url).once
end end
it "POSTs the data as JSON" do it "POSTs the data as JSON" do
json = @data.to_json json = @data.to_json
@webhook.execute(@data) @project_hook.execute(@data)
WebMock.should have_requested(:post, @webhook.url).with(body: json).once WebMock.should have_requested(:post, @project_hook.url).with(body: json).once
end end
it "catches exceptions" do it "catches exceptions" do
WebHook.should_receive(:post).and_raise("Some HTTP Post error") WebHook.should_receive(:post).and_raise("Some HTTP Post error")
lambda { lambda {
@webhook.execute(@data) @project_hook.execute(@data)
}.should_not raise_error }.should raise_error
end end
end end
end end
......
require 'spec_helper'
describe "Admin::Hooks" do
before do
@project = Factory :project,
:name => "LeGiT",
:code => "LGT"
login_as :admin
@system_hook = Factory :system_hook
end
describe "GET /admin/hooks" do
it "should be ok" do
visit admin_root_path
within ".main_menu" do
click_on "Hooks"
end
current_path.should == admin_hooks_path
end
it "should have hooks list" do
visit admin_hooks_path
page.should have_content(@system_hook.url)
end
end
describe "New Hook" do
before do
@url = Faker::Internet.uri("http")
visit admin_hooks_path
fill_in "hook_url", :with => @url
expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1)
end
it "should open new hook popup" do
page.current_path.should == admin_hooks_path
page.should have_content(@url)
end
end
describe "Test" do
before do
WebMock.stub_request(:post, @system_hook.url)
visit admin_hooks_path
click_link "Test Hook"
end
it { page.current_path.should == admin_hooks_path }
end
end
...@@ -13,9 +13,9 @@ describe "Admin::Projects" do ...@@ -13,9 +13,9 @@ describe "Admin::Projects" do
it { admin_users_path.should be_denied_for :visitor } it { admin_users_path.should be_denied_for :visitor }
end end
describe "GET /admin/emails" do describe "GET /admin/hooks" do
it { admin_emails_path.should be_allowed_for :admin } it { admin_hooks_path.should be_allowed_for :admin }
it { admin_emails_path.should be_denied_for :user } it { admin_hooks_path.should be_denied_for :user }
it { admin_emails_path.should be_denied_for :visitor } it { admin_hooks_path.should be_denied_for :visitor }
end end
end end
...@@ -9,7 +9,7 @@ describe "Hooks" do ...@@ -9,7 +9,7 @@ describe "Hooks" do
describe "GET index" do describe "GET index" do
it "should be available" do it "should be available" do
@hook = Factory :web_hook, :project => @project @hook = Factory :project_hook, :project => @project
visit project_hooks_path(@project) visit project_hooks_path(@project)
page.should have_content "Hooks" page.should have_content "Hooks"
page.should have_content @hook.url page.should have_content @hook.url
...@@ -21,7 +21,7 @@ describe "Hooks" do ...@@ -21,7 +21,7 @@ describe "Hooks" do
@url = Faker::Internet.uri("http") @url = Faker::Internet.uri("http")
visit project_hooks_path(@project) visit project_hooks_path(@project)
fill_in "hook_url", :with => @url fill_in "hook_url", :with => @url
expect { click_button "Add Web Hook" }.to change(WebHook, :count).by(1) expect { click_button "Add Web Hook" }.to change(ProjectHook, :count).by(1)
end end
it "should open new team member popup" do it "should open new team member popup" do
...@@ -32,7 +32,8 @@ describe "Hooks" do ...@@ -32,7 +32,8 @@ describe "Hooks" do
describe "Test" do describe "Test" do
before do before do
@hook = Factory :web_hook, :project => @project @hook = Factory :project_hook, :project => @project
stub_request(:post, @hook.url)
visit project_hooks_path(@project) visit project_hooks_path(@project)
click_link "Test Hook" click_link "Test Hook"
end end
......
...@@ -22,14 +22,14 @@ describe PostReceive do ...@@ -22,14 +22,14 @@ describe PostReceive do
Key.stub(find_by_identifier: nil) Key.stub(find_by_identifier: nil)
project.should_not_receive(:observe_push) project.should_not_receive(:observe_push)
project.should_not_receive(:execute_web_hooks) project.should_not_receive(:execute_hooks)
PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id).should be_false
end end
it "asks the project to execute web hooks" do it "asks the project to execute web hooks" do
Project.stub(find_by_path: project) Project.stub(find_by_path: project)
project.should_receive(:execute_web_hooks).with('sha-old', 'sha-new', 'refs/heads/master', project.owner) project.should_receive(:execute_hooks).with('sha-old', 'sha-new', 'refs/heads/master', project.owner)
PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id) PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master', key_id)
end end
......
/*
* waitForImages 1.4
* -----------------
* Provides a callback when all images have loaded in your given selector.
* http://www.alexanderdickson.com/
*
*
* Copyright (c) 2011 Alex Dickson
* Licensed under the MIT licenses.
* See website for more info.
*
*/
;(function($) {
// Namespace all events.
var eventNamespace = 'waitForImages';
// CSS properties which contain references to images.
$.waitForImages = {
hasImageProperties: [
'backgroundImage',
'listStyleImage',
'borderImage',
'borderCornerImage'
]
};
// Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
$.expr[':'].uncached = function(obj) {
// Ensure we are dealing with an `img` element with a valid `src` attribute.
if ( ! $(obj).is('img[src!=""]')) {
return false;
}
// Firefox's `complete` property will always be`true` even if the image has not been downloaded.
// Doing it this way works in Firefox.
var img = document.createElement('img');
img.src = obj.src;
return ! img.complete;
};
$.fn.waitForImages = function(finishedCallback, eachCallback, waitForAll) {
// Handle options object.
if ($.isPlainObject(arguments[0])) {
eachCallback = finishedCallback.each;
waitForAll = finishedCallback.waitForAll;
finishedCallback = finishedCallback.finished;
}
// Handle missing callbacks.
finishedCallback = finishedCallback || $.noop;
eachCallback = eachCallback || $.noop;
// Convert waitForAll to Boolean
waitForAll = !! waitForAll;
// Ensure callbacks are functions.
if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
throw new TypeError('An invalid callback was supplied.');
};
return this.each(function() {
// Build a list of all imgs, dependent on what images will be considered.
var obj = $(this),
allImgs = [];
if (waitForAll) {
// CSS properties which may contain an image.
var hasImgProperties = $.waitForImages.hasImageProperties || [],
matchUrl = /url\((['"]?)(.*?)\1\)/g;
// Get all elements, as any one of them could have a background image.
obj.find('*').each(function() {
var element = $(this);
// If an `img` element, add it. But keep iterating in case it has a background image too.
if (element.is('img:uncached')) {
allImgs.push({
src: element.attr('src'),
element: element[0]
});
}
$.each(hasImgProperties, function(i, property) {
var propertyValue = element.css(property);
// If it doesn't contain this property, skip.
if ( ! propertyValue) {
return true;
}
// Get all url() of this element.
var match;
while (match = matchUrl.exec(propertyValue)) {
allImgs.push({
src: match[2],
element: element[0]
});
};
});
});
} else {
// For images only, the task is simpler.
obj
.find('img:uncached')
.each(function() {
allImgs.push({
src: this.src,
element: this
});
});
};
var allImgsLength = allImgs.length,
allImgsLoaded = 0;
// If no images found, don't bother.
if (allImgsLength == 0) {
finishedCallback.call(obj[0]);
};
$.each(allImgs, function(i, img) {
var image = new Image;
// Handle the image loading and error with the same callback.
$(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function(event) {
allImgsLoaded++;
// If an error occurred with loading the image, set the third argument accordingly.
eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
if (allImgsLoaded == allImgsLength) {
finishedCallback.call(obj[0]);
return false;
};
});
image.src = img.src;
});
});
};
})(jQuery);
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