Commit eca823c1 authored by Nihad Abbasov's avatar Nihad Abbasov

Merge branch 'master' into api

parents 024e0348 8b7e404b
......@@ -6,6 +6,7 @@ log/*.log
tmp/
.sass-cache/
coverage/*
backups/*
*.swp
public/uploads/
.rvmrc
......
v 2.7.0
- Issue Labels
- Inline diff
- Git HTTP
- API
- UI improved
- System hooks
- UI improved
- Dashboard events endless scroll
- Source perfomance increased
v 2.6.0
- UI polished
......
......@@ -7,7 +7,7 @@ gem "sqlite3"
gem "mysql2"
# Auth
gem "devise", "~> 1.5"
gem "devise", "~> 2.1.0"
# GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
......@@ -71,7 +71,6 @@ group :development, :test do
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
gem "webmock"
end
group :test do
......@@ -82,4 +81,5 @@ group :test do
gem "shoulda-matchers"
gem 'email_spec'
gem 'resque_spec'
gem "webmock"
end
......@@ -148,10 +148,11 @@ GEM
nokogiri (>= 1.5.0)
daemons (1.1.8)
database_cleaner (0.8.0)
devise (1.5.3)
devise (2.1.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.0.3)
warden (~> 1.1)
orm_adapter (~> 0.1)
railties (~> 3.1)
warden (~> 1.2.1)
diff-lcs (1.1.3)
drapper (0.8.4)
email_spec (1.2.1)
......@@ -225,7 +226,7 @@ GEM
omniauth (1.1.0)
hashie (~> 1.2)
rack
orm_adapter (0.0.7)
orm_adapter (0.3.0)
polyglot (0.3.3)
posix-spawn (0.3.6)
pry (0.9.9.6)
......@@ -356,7 +357,7 @@ GEM
raindrops (~> 0.7)
vegas (0.1.11)
rack (>= 1.0.0)
warden (1.2.0)
warden (1.2.1)
rack (>= 1.0)
webmock (1.8.7)
addressable (>= 2.2.7)
......@@ -383,7 +384,7 @@ DEPENDENCIES
colored
cucumber-rails
database_cleaner
devise (~> 1.5)
devise (~> 2.1.0)
drapper
email_spec
ffaker
......
......@@ -12,6 +12,7 @@
//= require jquery.cookie
//= require jquery.endless-scroll
//= require jquery.highlight
//= require jquery.waitforimages
//= require bootstrap-modal
//= require modernizr
//= require chosen-jquery
......@@ -20,10 +21,26 @@
//= require_tree .
$(document).ready(function(){
$(".one_click_select").live("click", function(){
$(this).select();
});
$('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
var buttons = $('[type="submit"]', this);
switch( e.type ){
case 'ajax:beforeSend':
case 'submit':
buttons.attr('disabled', 'disabled');
break;
case ' ajax:complete':
default:
buttons.removeAttr('disabled');
break;
}
})
$(".account-box").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu);
......@@ -97,3 +114,8 @@ function showDiff(link) {
return _chosen.apply(this, [default_options]);
}})
})(jQuery);
function ajaxGet(url) {
$.ajax({type: "GET", url: url, dataType: "script"});
}
......@@ -73,4 +73,25 @@ function issuesPage(){
$("#milestone_id, #assignee_id, #label_name").on("change", function(){
$(this).closest("form").submit();
});
$('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
var t = $(this),
totalIssues,
reopen = t.hasClass('reopen_issue'),
newIssue = false;
if( this.id == 'new_issue' ){
newIssue = true;
}
$('.issue_counter, #new_issue').each(function(){
var issue = $(this);
totalIssues = parseInt( $(this).html(), 10 );
if( newIssue || ( reopen && issue.closest('.main_menu').length ) ){
$(this).html( totalIssues+1 );
}else {
$(this).html( totalIssues-1 );
}
});
});
}
......@@ -25,11 +25,11 @@ init:
$(this).closest('li').fadeOut(); });
$("#new_note").live("ajax:before", function(){
$("#submit_note").attr("disabled", "disabled");
$(".submit_note").attr("disabled", "disabled");
})
$("#new_note").live("ajax:complete", function(){
$("#submit_note").removeAttr("disabled");
$(".submit_note").removeAttr("disabled");
})
$("#note_note").live("focus", function(){
......
......@@ -604,7 +604,11 @@ li.note {
border-style: solid;
border-width: 1px;
@include border-radius(4px);
min-height:42px;
min-height:22px;
.avatar {
width:24px;
}
}
.supp_diff_link,
......
......@@ -202,6 +202,10 @@ a:focus {
color:$style_color;
}
.nav-tabs > .active > a {
font-weight:bold;
}
/** COLORS **/
.cgray { color:gray; }
.cred { color:#D12F19; }
......@@ -209,6 +213,7 @@ a:focus {
.cblack { color:#111; }
.cdark { color:#444 }
.cwhite { color:#fff !important }
.bgred { background: #F2DEDE !important}
/** COMMON STYLES **/
.left {
......@@ -299,9 +304,24 @@ table.no-borders {
}
.event_label {
background: #FCEEC1;
padding: 2px 2px 0;
font-family: monospace;
@extend .label;
background-color: #999;
&.pushed {
background-color: #3A87AD;
}
&.opened {
background-color: #468847;
}
&.closed {
background-color: #B94A48;
}
&.merged {
background-color: #2A2;
}
}
img.avatar {
......@@ -425,9 +445,10 @@ form {
*/
.ui-box {
background:#F9F9F9;
margin-bottom: 40px;
margin-bottom: 25px;
@include round-borders-all(4px);
border-color: #CCC;
@include solid_shade;
ul {
margin:0;
......@@ -443,6 +464,13 @@ form {
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
&.small {
line-height: 28px;
font-size: 14px;
line-height:28px;
text-shadow: 0 1px 1px white;
}
form {
padding:9px 0;
margin:0px;
......@@ -511,6 +539,7 @@ form {
table.admin-table {
@extend .table-bordered;
@extend .zebra-striped;
@include solid_shade;
th {
border-color: #CCC;
border-bottom: 1px solid #bbb;
......@@ -568,6 +597,8 @@ ul.breadcrumb {
@extend .prepend-top-20;
@extend .append-bottom-20;
border-width:1px;
@include solid_shade;
img { max-width: 100%; }
......@@ -624,13 +655,166 @@ p {
h3.page_title {
color:#456;
font-size:20px;
font-weight: 600;
font-weight: normal;
line-height: 28px;
}
pre.logs {
.log {
font-size:12px;
line-height:18px;
/**
* File content holder
*
*/
.file_holder {
border:1px solid #CCC;
margin-bottom:1em;
@include solid_shade;
.file_title {
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
padding: 9px 10px;
height:18px;
.options {
float:right;
margin-top: -5px;
}
.file_name {
color:$style_color;
font-size:14px;
text-shadow: 0 1px 1px #fff;
small {
color:#999;
font-size:13px;
}
}
}
.file_content {
background:#fff;
font-size: 11px;
&.wiki {
font-size: 13px;
code {
padding:0 4px;
}
padding:20px;
h1, h2 {
line-height: 46px;
}
h3, h4 {
line-height: 40px;
}
}
&.image_file {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
&.blob_file {
}
/**
* Blame file
*/
&.blame {
tr {
border-bottom: 1px solid #eee;
}
td {
padding:5px;
}
.author,
.blame_commit {
background:#f5f5f5;
vertical-align:top;
}
.lines {
pre {
padding:0;
margin:0;
background:none;
border:none;
}
}
}
&.logs {
background:#eee;
max-height: 700px;
overflow-y: auto;
ol {
margin-left:40px;
padding: 10px 0;
border-left: 1px solid #CCC;
margin-bottom:0;
background: white;
li {
color:#888;
p {
margin:0;
color:#333;
line-height:24px;
padding-left: 10px;
}
&:hover {
background:$hover;
}
}
}
}
/**
* Code file
*/
&.code {
padding:0;
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#666;
}
}
}
}
......@@ -96,7 +96,7 @@ header {
*/
.search {
float: right;
margin-right: 55px;
margin-right: 50px;
.search-input {
@extend .span2;
......@@ -126,10 +126,10 @@ header {
cursor: pointer;
img {
border-radius: 4px;
right: 0px;
right: 5px;
position: absolute;
width: 33px;
height: 33px;
width: 31px;
height: 31px;
display: block;
top: 0;
&:after {
......
......@@ -31,6 +31,12 @@ $hover: #FDF5D9;
box-shadow: 0 0 3px #ddd;
}
@mixin solid_shade {
-moz-box-shadow: 0 0 0 3px #eee;
-webkit-box-shadow: 0 0 0 3px #eee;
box-shadow: 0 0 0 3px #eee;
}
@mixin border-radius($radius) {
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
......@@ -136,7 +142,7 @@ $hover: #FDF5D9;
/**
* Code (files list) styles. Browsing project files there
*/
@import "tree.scss";
@import "sections/tree.scss";
/**
* This file represent notes(comments) styles
......
......@@ -63,18 +63,22 @@ p.notify_controls span{
tr.line_notes_row {
border-bottom:1px solid #DDD;
border-left: 7px solid #2A79A3;
&.reply {
background:#eee;
border-left: 7px solid #2A79A3;
border-top:1px solid #ddd;
td {
padding:7px 10px;
}
a.line_note_reply_link {
@include round-borders-all(4px);
border-color:#aaa;
background: #bbb;
padding: 3px 20px;
padding: 3px 10px;
margin-left:5px;
color: white;
background: #2A79A3;
border-color: #2A79A3;
}
}
ul {
......@@ -95,6 +99,9 @@ tr.line_notes_row {
td {
border-bottom:1px solid #ddd;
}
.actions {
margin:0;
}
}
td .line_note_link {
......
......@@ -101,18 +101,21 @@
margin:50px;
padding:1px;
max-width:400px;
}
&.diff_image_removed {
img {
&.diff_image_removed {
border: 1px solid #C00;
}
}
&.diff_image_added {
img {
&.diff_image_added {
border: 1px solid #0C0;;
}
}
&.img_compared {
img {
max-width:300px;
}
}
}
}
......
......@@ -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 @@
}
}
/** FILE CONTENT VIEW **/
.view_file_content{
.old_line, .new_line {
background:#ECECEC;
color:#777;
width:15px;
float:left;
padding: 0px 10px;
border-right: 1px solid #ccc;
}
.old_line{
display:none;
}
}
.view_file .view_file_header,
.diff_file .diff_file_header {
border-bottom: 1px solid #bbb;
background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
padding: 9px 10px;
height:18px;
.options {
float:right;
margin-top: -5px;
}
.file_name {
color:$style_color;
font-size:14px;
text-shadow: 0 1px 1px #fff;
small {
color:#999;
font-size:13px;
}
}
}
.view_file {
border:1px solid #CCC;
margin-bottom:1em;
.view_file_content {
background:#fff;
color:#514721;
font-size: 11px;
}
.view_file_content_image {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
}
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#666;
}
#tree-slider {
@include border-radius(0);
.tree-item {
......@@ -152,7 +55,7 @@
#tree-slider {
@include shade;
@include solid_shade;
width:100%;
border-color:#ccc;
......@@ -183,21 +86,6 @@
color:#333;
}
#tree-content-holder .view_file{
@include shade;
}
#tree-readme-holder .readme {
@include shade;
margin-bottom:20px;
h1, h2 {
line-height: 56px;
}
h3, h4 {
line-height: 46px;
}
}
a.tree-commit-link {
color: #666;
&:hover {
......@@ -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 @@
}
}
.separator {
border-color:#444;
background:#31363E;
display:none;
}
}
......
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
def index
@admin_projects = Project.scoped
@admin_projects = @admin_projects.search(params[:name]) if params[:name].present?
@admin_projects = @admin_projects.page(params[:page])
@admin_projects = @admin_projects.page(params[:page]).per(20)
end
def show
......@@ -72,6 +72,6 @@ class Admin::ProjectsController < ApplicationController
@admin_project = Project.find_by_code(params[:id])
@admin_project.destroy
redirect_to admin_projects_url
redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
end
end
......@@ -52,7 +52,7 @@ class ApplicationController < ActionController::Base
def layout_by_resource
if devise_controller?
"devise"
"devise_layout"
else
"application"
end
......
......@@ -26,43 +26,31 @@ class CommitsController < ApplicationController
end
def show
@commit = project.commit(params[:id])
git_not_found! and return unless @commit
@commit = CommitDecorator.decorate(@commit)
@note = @project.build_commit_note(@commit)
@comments_allowed = true
@line_notes = project.commit_line_notes(@commit)
@notes_count = @line_notes.count + project.commit_notes(@commit).count
if @commit.diffs.size > 200 && !params[:force_show_diff]
@suppress_diff = true
result = CommitLoad.new(project, current_user, params).execute
@commit = result[:commit]
if @commit
@suppress_diff = result[:suppress_diff]
@note = result[:note]
@line_notes = result[:line_notes]
@notes_count = result[:notes_count]
@comments_allowed = true
else
return git_not_found!
end
rescue Grit::Git::GitTimeout
render "huge_commit"
end
def compare
first = project.commit(params[:to].try(:strip))
last = project.commit(params[:from].try(:strip))
result = Commit.compare(project, params[:from], params[:to])
@diffs = []
@commits = []
@commits = result[:commits]
@commit = result[:commit]
@diffs = result[:diffs]
@line_notes = []
if first && last
commits = [first, last].sort_by(&:created_at)
younger = commits.first
older = commits.last
@commits = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
@diffs = project.repo.diff(younger.id, older.id) rescue []
@commit = Commit.new(older)
end
end
def patch
......
......@@ -2,15 +2,13 @@ class DashboardController < ApplicationController
respond_to :html
def index
@projects = current_user.projects.includes(:events).order("events.created_at DESC")
@projects = @projects.page(params[:page]).per(40)
@events = Event.where(:project_id => current_user.projects.map(&:id)).recent.limit(20)
@projects = current_user.projects_with_events.page(params[:page]).per(40)
@events = Event.recent_for_user(current_user).limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push
respond_to do |format|
format.html
format.js
format.atom { render :layout => false }
end
end
......
......@@ -11,24 +11,24 @@ class HooksController < ApplicationController
respond_to :html
def index
@hooks = @project.web_hooks.all
@hook = WebHook.new
@hooks = @project.hooks.all
@hook = ProjectHook.new
end
def create
@hook = @project.web_hooks.new(params[:hook])
@hook = @project.hooks.new(params[:hook])
@hook.save
if @hook.valid?
redirect_to project_hooks_path(@project)
else
@hooks = @project.web_hooks.all
@hooks = @project.hooks.all
render :index
end
end
def test
@hook = @project.web_hooks.find(params[:id])
@hook = @project.hooks.find(params[:id])
commits = @project.commits(@project.default_branch, nil, 3)
data = @project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}", current_user)
@hook.execute(data)
......@@ -37,7 +37,7 @@ class HooksController < ApplicationController
end
def destroy
@hook = @project.web_hooks.find(params[:id])
@hook = @project.hooks.find(params[:id])
@hook.destroy
redirect_to project_hooks_path(@project)
......
......@@ -24,16 +24,7 @@ class MergeRequestsController < ApplicationController
def index
@merge_requests = @project.merge_requests
@merge_requests = case params[:f].to_i
when 1 then @merge_requests
when 2 then @merge_requests.closed
when 3 then @merge_requests.opened.assigned(current_user)
else @merge_requests.opened
end.page(params[:page]).per(20)
@merge_requests = @merge_requests.includes(:author, :project).order("closed, created_at desc")
@merge_requests = MergeRequestsLoad.new(project, current_user, params).execute
end
def show
......
......@@ -40,25 +40,6 @@ class NotesController < ApplicationController
protected
def notes
@notes = case params[:target_type]
when "commit"
then project.commit_notes(project.commit((params[:target_id]))).fresh.limit(20)
when "snippet"
then project.snippets.find(params[:target_id]).notes
when "wall"
then project.common_notes.order("created_at DESC").fresh.limit(50)
when "issue"
then project.issues.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20)
when "merge_request"
then project.merge_requests.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20)
end
@notes = if params[:last_id]
@notes.where("id > ?", params[:last_id])
elsif params[:first_id]
@notes.where("id < ?", params[:first_id])
else
@notes
end
@notes = NotesLoad.new(project, current_user, params).execute
end
end
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Extend the standard message generation to accept our custom exception
def failure_message
exception = env["omniauth.error"]
if exception.class == OmniAuth::Error
error = exception.message
else
error = exception.error_reason if exception.respond_to?(:error_reason)
error ||= exception.error if exception.respond_to?(:error)
error ||= env["omniauth.error.type"].to_s
end
error.to_s.humanize if error
end
def ldap
# We only find ourselves here if the authentication to LDAP was successful.
......
......@@ -9,7 +9,7 @@ class RefsController < ApplicationController
before_filter :require_non_empty_project
before_filter :ref
before_filter :define_tree_vars, :only => [:tree, :blob, :blame]
before_filter :define_tree_vars, :only => [:tree, :blob, :blame, :logs_tree]
before_filter :render_full_content
layout "project"
......@@ -46,6 +46,18 @@ class RefsController < ApplicationController
end
end
def logs_tree
contents = @tree.contents
@logs = contents.map do |content|
file = params[:path] ? File.join(params[:path], content.name) : content.name
last_commit = @project.commits(@commit.id, file, 1).last
{
:file_name => content.name,
:commit => last_commit
}
end
end
def blob
if @tree.is_blob?
if @tree.text?
......@@ -79,6 +91,15 @@ class RefsController < ApplicationController
@commit = project.commit(@ref)
@tree = Tree.new(@commit.tree, project, @ref, params[:path])
@tree = TreeDecorator.new(@tree)
@hex_path = Digest::SHA1.hexdigest(params[:path] || "/")
if params[:path]
@history_path = tree_file_project_ref_path(@project, @ref, params[:path])
@logs_path = logs_file_project_ref_path(@project, @ref, params[:path])
else
@history_path = tree_project_ref_path(@project, @ref)
@logs_path = logs_tree_project_ref_path(@project, @ref)
end
rescue
return render_404
end
......
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
end
active ? "current" : nil
end
def hexdigest(string)
Digest::SHA1.hexdigest string
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
def commits_between(repo, from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
def compare(project, from, to)
first = project.commit(to.try(:strip))
last = project.commit(from.try(:strip))
result = {
:commits => [],
:diffs => [],
:commit => nil
}
if first && last
commits = [first, last].sort_by(&:created_at)
younger = commits.first
older = commits.last
result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older)
end
result
end
end
def persisted?
......
......@@ -28,6 +28,10 @@ class Event < ActiveRecord::Base
end
end
def self.recent_for_user user
where(:project_id => user.projects.map(&:id)).recent
end
# Next events currently enabled for system
# - push
# - new issue
......
......@@ -22,7 +22,6 @@ class MergeRequest < ActiveRecord::Base
:should_remove_source_branch
validates_presence_of :project_id
validates_presence_of :assignee_id
validates_presence_of :author_id
validates_presence_of :source_branch
validates_presence_of :target_branch
......@@ -36,6 +35,7 @@ class MergeRequest < ActiveRecord::Base
delegate :name,
:email,
:to => :assignee,
:allow_nil => true,
:prefix => true
validates :title,
......@@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base
def unmerged_diffs
commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id)
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
end
def last_commit
......
......@@ -19,7 +19,7 @@ class Project < ActiveRecord::Base
has_many :notes, :dependent => :destroy
has_many :snippets, :dependent => :destroy
has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
has_many :web_hooks, :dependent => :destroy
has_many :hooks, :dependent => :destroy, :class_name => "ProjectHook"
has_many :wikis, :dependent => :destroy
has_many :protected_branches, :dependent => :destroy
......@@ -120,7 +120,7 @@ class Project < ActiveRecord::Base
errors.add(:path, " like 'gitolite-admin' is not allowed")
end
end
def self.access_options
UsersProject.access_roles
end
......
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
include Account
devise :database_authenticatable, :token_authenticatable,
devise :database_authenticatable, :token_authenticatable, :lockable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
:theme_id, :force_random_password
attr_accessor :force_random_password
......@@ -15,6 +16,11 @@ class User < ActiveRecord::Base
has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id
has_many :keys, :dependent => :destroy
has_many :events,
:class_name => "Event",
:foreign_key => :author_id,
:dependent => :destroy
has_many :recent_events,
:class_name => "Event",
:foreign_key => :author_id,
......@@ -80,7 +86,8 @@ class User < ActiveRecord::Base
def self.find_for_ldap_auth(omniauth_info)
name = omniauth_info.name.force_encoding("utf-8")
email = omniauth_info.email.downcase
email = omniauth_info.email.downcase unless omniauth_info.email.nil?
raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil?
if @user = User.find_by_email(email)
@user
......
......@@ -68,7 +68,7 @@ class UsersProject < ActiveRecord::Base
end
def repo_access_human
""
self.class.access_roles.invert[self.project_access]
end
end
# == Schema Information
......
......@@ -4,8 +4,6 @@ class WebHook < ActiveRecord::Base
# HTTParty timeout
default_timeout 10
belongs_to :project
validates :url,
presence: true,
format: {
......@@ -14,9 +12,8 @@ class WebHook < ActiveRecord::Base
def execute(data)
WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
rescue
# There was a problem calling this web hook, let's forget about it.
end
end
# == Schema Information
#
......
......@@ -43,7 +43,7 @@ class MailerObserver < ActiveRecord::Observer
end
def new_merge_request(merge_request)
if merge_request.assignee != current_user
if merge_request.assignee && merge_request.assignee != current_user
Notify.new_merge_request_email(merge_request.id).deliver
end
end
......
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
# Take only latest one
events = events.recent.limit(1).first
end
def projects_with_events
projects.includes(:events).order("events.created_at DESC")
end
end
......@@ -27,7 +27,7 @@ module GitPush
true
end
def execute_web_hooks(oldrev, newrev, ref, user)
def execute_hooks(oldrev, newrev, ref, user)
ref_parts = ref.split('/')
# Return if this is not a push to a branch (e.g. new commits)
......@@ -35,7 +35,7 @@ module GitPush
data = post_receive_data(oldrev, newrev, ref, user)
web_hooks.each { |web_hook| web_hook.execute(data) }
hooks.each { |hook| hook.execute(data) }
end
def post_receive_data(oldrev, newrev, ref, user)
......@@ -97,7 +97,7 @@ module GitPush
self.update_merge_requests(oldrev, newrev, ref, user)
# Execute web hooks
self.execute_web_hooks(oldrev, newrev, ref, user)
self.execute_hooks(oldrev, newrev, ref, user)
# Create satellite
self.satellite.create unless self.satellite.exists?
......
<% 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
%i.icon-file
githost.log
%pre.logs
- Gitlab::Logger.read_latest.each do |line|
%span.log= line
.file_holder#README
.file_title
%i.icon-file
githost.log
.file_content.logs
%ol
- Gitlab::Logger.read_latest.each do |line|
%li
%p= line
%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 @@
%th Team Members
%th Post Receive
%th Last Commit
%th
%th
%th Edit
%th.cred Danger Zone!
- @admin_projects.each do |project|
%tr
......@@ -24,5 +24,5 @@
%td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, :disabled => true
%td= last_commit(project)
%td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}", :class => "btn small"
%td= link_to 'Destroy', [:admin, project], :confirm => 'Are you sure?', :method => :delete, :class => "btn small danger"
%td.bgred= link_to 'Destroy', [:admin, project], :confirm => "REMOVE #{project.name}? Are you sure?", :method => :delete, :class => "btn small danger"
= paginate @admin_projects, :theme => "admin"
......@@ -50,7 +50,7 @@
.alert
.clearfix
%p Give user ability to manage application.
%p Make the user a GitLab administrator.
= f.label :admin, :class => "checkbox" do
= f.check_box :admin
%span Administrator
......@@ -59,11 +59,11 @@
- if @admin_user.blocked
%span
= link_to 'Unblock', unblock_admin_user_path(@admin_user), :method => :put, :class => "btn small"
This user is blocked and is not able to login GitLab
This user is blocked and is not able to login to GitLab
- else
%span
= link_to 'Block', block_admin_user_path(@admin_user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger"
Blocked user will removed from all projects &amp; will not be able to login to GitLab.
Blocked users will be removed from all projects &amp; will not be able to login to GitLab.
.actions
= f.submit 'Save', :class => "btn primary"
- if @admin_user.new_record?
......
......@@ -27,7 +27,7 @@
%th Projects
%th Edit
%th Blocked
%th
%th.cred Danger Zone!
- @admin_users.each do |user|
%tr
......@@ -41,6 +41,6 @@
= link_to 'Unblock', unblock_admin_user_path(user), :method => :put, :class => "btn small success"
- else
= link_to 'Block', block_admin_user_path(user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger"
%td= link_to 'Destroy', [:admin, user], :confirm => 'USER WILL BE REMOVED! Are you sure?', :method => :delete, :class => "btn small danger"
%td.bgred= link_to 'Destroy', [:admin, user], :confirm => "USER #{user.name} WILL BE REMOVED! Are you sure?", :method => :delete, :class => "btn small danger"
= paginate @admin_users, :theme => "admin"
- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits|
%div.ui-box
%h5= day.stamp("28 Aug, 2010")
%h5.small
%i.icon-calendar
= day.stamp("28 Aug, 2010")
%ul.unstyled= render commits
......@@ -35,7 +35,13 @@
- if file.text?
= render "commits/text_file", :diff => diff, :index => i
- elsif file.image?
.diff_file_content_image{:class => image_diff_class(diff)}
%img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- if diff.renamed_file || diff.new_file || diff.deleted_file
.diff_file_content_image
%img{:class => image_diff_class(diff), :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else
- old_file = (@commit.prev_commit.tree / diff.old_path)
.diff_file_content_image.img_compared
%img{:class => "diff_image_removed", :src => "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
%img{:class => "diff_image_added", :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else
%p.nothing_here_message No preview for this file type
......@@ -13,12 +13,12 @@
%li{:class => "#{branches_tab_class}"}
= link_to project_repository_path(@project) do
Branches
%span.number= @project.repo.branch_count
%span.badge= @project.repo.branch_count
%li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"}
= link_to tags_project_repository_path(@project) do
Tags
%span.number= @project.repo.tag_count
%span.badge= @project.repo.tag_count
- if current_page?(project_commits_path(@project)) && current_user.private_token
......
......@@ -20,7 +20,7 @@
= "..."
= text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge"
.actions
= submit_tag "Compare", :class => "btn primary"
= submit_tag "Compare", :class => "btn btn-primary"
- unless @commits.empty?
......
......@@ -8,17 +8,10 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
@events.each do |event|
if event.allowed?
event = EventDecorator.decorate(event)
xml.entry do
if event.issue?
event_link = project_issue_url(event.project, event.issue)
event_title = event.issue_title
elsif event.merge_request?
event_link = project_merge_request_url(event.project, event.merge_request)
event_title = event.merge_request_title
elsif event.push?
event_link = project_commits_url(event.project, :ref => event.ref_name)
event_title = event.ref_name
end
event_link = event.feed_url
event_title = event.feed_title
xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
xml.link :href => event_link
......
......@@ -10,9 +10,10 @@
add new key
to your profile
- if @events.any?
= render @events
.content_list= render @events
- else
%h4.nothing_here_message Projects activity will be displayed here
.loading.hide
.side
= render "events/event_last_push", :event => @last_push
.projects_box
......@@ -54,3 +55,7 @@
New Project »
- else
If you will be added to project - it will be displayed here
:javascript
$(function(){ Pager.init(20); });
: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"
%strong #{event.author_name}
%span.event_label= event.action_name
&nbsp;issue
%span.event_label{:class => event.action_name}= event.action_name
issue
= link_to project_issue_path(event.project, event.issue) do
%strong= truncate event.issue_title
at
......
......@@ -5,12 +5,9 @@
%span Your pushed to
= event.ref_type
= link_to project_commits_path(event.project, :ref => event.ref_name) do
%strong= event.ref_name
%strong= truncate(event.ref_name, :length => 28)
at
%strong= link_to event.project.name, event.project
%span.cgray
= time_ago_in_words(event.created_at)
ago.
= link_to new_mr_path_from_push_event(event), :title => "New Merge Request", :class => "btn very_small primary" do
Create Merge Request
......@@ -2,8 +2,8 @@
.event_icon= image_tag "event_mr_merged.png"
= image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name}
%span.event_label= event.action_name
&nbsp;merge request
%span.event_label{:class => event.action_name}= event.action_name
merge request
= link_to project_merge_request_path(event.project, event.merge_request) do
%strong= truncate event.merge_request_title
at
......
......@@ -2,7 +2,7 @@
.event_icon= image_tag "event_push.png"
= image_tag gravatar_icon(event.author_email), :class => "avatar"
%strong #{event.author_name}
%span.event_label= event.push_action_name
%span.event_label.pushed= event.push_action_name
= event.ref_type
= link_to project_commits_path(event.project, :ref => event.ref_name) do
%strong= event.ref_name
......
%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 @@
%li
%h5= link_to "Web Hooks", help_web_hooks_path
%li
%h5= link_to "System Hooks", help_system_hooks_path
%li
%h5= link_to "API", help_api_path
%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 @@
.row
.span7= paginate @issues, :remote => true, :theme => "gitlab"
.span3.right
%span.cgray.right #{@issues.total_count} issues for this filter
%span.cgray.right
%span.issue_counter #{@issues.total_count}
issues for this filter
- else
%li
%h4.nothing_here_message Nothing to show here
......@@ -12,9 +12,9 @@
= issue.notes.count
- if can? current_user, :modify_issue, issue
- if issue.closed
= link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped", :remote => true
= link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped reopen_issue", :remote => true
- else
= link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped", :remote => true
= link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped close_issue", :remote => true
= link_to edit_project_issue_path(issue.project, issue), :class => "btn small edit-issue-link", :remote => true do
%i.icon-edit
Edit
......@@ -35,6 +35,4 @@
&nbsp;
- if issue.upvotes > 0
%span.badge.badge-success= "+#{issue.upvotes}"
%span.badge.badge-success= "+#{issue.upvotes}"
\ No newline at end of file
......@@ -2,7 +2,7 @@
.issues_content
%h3.page_title
Issues
%small (#{@issues.total_count})
%small (<span class=issue_counter>#{@issues.total_count}</span>)
.right
.span5
- if can? current_user, :write_issue, @project
......@@ -45,4 +45,4 @@
:javascript
$(function(){
issuesPage();
})
})
\ No newline at end of file
%h3 New key
%h3.page_title New key
%hr
= render 'form'
......@@ -11,4 +11,4 @@
if( key_mail && key_mail.length > 0 && title.val() == '' ){
$('#key_title').val( key_mail );
}
});
\ No newline at end of file
});
......@@ -17,14 +17,14 @@
%li{:class => tab_class(:issues)}
= link_to project_issues_filter_path(@project) do
Issues
%span.count= @project.issues.opened.count
%span.count.issue_counter= @project.issues.opened.count
- if @project.repo_exists?
- if @project.merge_requests_enabled
%li{:class => tab_class(:merge_requests)}
= link_to project_merge_requests_path(@project) do
Merge Requests
%span.count= @project.merge_requests.opened.count
%span.count.merge_counter= @project.merge_requests.opened.count
- if @project.wall_enabled
%li{:class => tab_class(:wall)}
......
......@@ -15,7 +15,7 @@
%li{:class => tab_class(:admin_logs)}
= link_to "Logs", admin_logs_path
%li{:class => tab_class(:admin_emails)}
= link_to "Emails", admin_emails_path
= link_to "Hooks", admin_hooks_path
%li{:class => tab_class(:admin_resque)}
= link_to "Resque", admin_resque_path
......
......@@ -12,16 +12,17 @@
%li{:class => tab_class(:password)}
= link_to "Password", profile_password_path
%li{:class => tab_class(:ssh_keys)}
= link_to keys_path do
SSH Keys
%span.count= current_user.keys.count
%li{:class => tab_class(:token)}
= link_to "Token", profile_token_path
%li{:class => tab_class(:design)}
= link_to "Design", profile_design_path
%li{:class => tab_class(:ssh_keys)}
= link_to keys_path do
SSH Keys
%span.count= current_user.keys.count
.content
= yield
......@@ -5,7 +5,8 @@
- @merge_request.errors.full_messages.each do |msg|
%li= msg
%h3.padded.cgray 1. Select Branches
%h4.cdark 1. Select Branches
%br
.row
.span6
......@@ -30,14 +31,21 @@
.bottom_commit
.mr_target_commit
%h3.padded.cgray 2. Fill info
%h4.cdark 2. Fill info
.clearfix
= f.label :assignee_id, "Assign to", :class => "control-label"
.controls= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px")
.main_box
.top_box_content
= f.label :title do
%strong= "Title *"
.input= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5
.middle_box_content
= f.label :assignee_id do
%i.icon-user
Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px")
.control-group
= f.label :title, :class => "control-label"
.controls= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5
.form-actions
= f.submit 'Save', :class => "btn-primary btn"
......
......@@ -15,12 +15,14 @@
&rarr;
= merge_request.target_branch
= image_tag gravatar_icon(merge_request.author_email), :class => "avatar"
= link_to project_merge_request_path(merge_request.project, merge_request) do
%p.row_title= truncate(merge_request.title, :length => 80)
%span.update-author
%strong= merge_request.author_name
authored
%small.cdark= "##{merge_request.id}"
authored by #{merge_request.author_name}
= time_ago_in_words(merge_request.created_at)
ago
- if merge_request.upvotes > 0
%span.badge.badge-success= "+#{merge_request.upvotes}"
= link_to project_merge_request_path(merge_request.project, merge_request) do
%p.row_title= truncate(merge_request.title, :length => 80)
%h3
%h3.page_title
= "Edit merge request #{@merge_request.id}"
%hr
= render 'form'
%h3 New Merge Request
%h3.page_title New Merge Request
%hr
= render 'form'
- if @commits.present?
.ui-box
%h5 Commits (#{@commits.count})
%h5
%i.icon-list
Commits (#{@commits.count})
.merge-request-commits
- if @commits.count > 8
%ul.first_mr_commits.unstyled
......
......@@ -13,9 +13,10 @@
= image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_author(@merge_request)
%cite.cgray and currently assigned to
= image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_assignee(@merge_request)
- if @merge_request.assignee
%cite.cgray and currently assigned to
= image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av"
%strong.author= link_to_merge_request_assignee(@merge_request)
- if @merge_request.closed
......
......@@ -32,4 +32,4 @@
%span Any file less than 10 MB
= f.submit 'Add Comment', :class => "btn primary", :id => "submit_note"
= f.submit 'Add Comment', :class => "btn primary submit_note", :id => "submit_note"
......@@ -24,7 +24,7 @@
= check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
%span Commit author
.actions
= f.submit 'Add note', :class => "btn primary", :id => "submit_note"
= f.submit 'Add note', :class => "btn primary submit_note", :id => "submit_note"
= link_to "Close", "#", :class => "btn hide-button"
:javascript
......
%tr.line_notes_row.reply
%td{:colspan => 3}
%i.icon-comment
= link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line"
......@@ -13,7 +13,7 @@
= render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree }
- else
- contents = tree.contents
%table#tree-slider.bordered-table.table
%table#tree-slider.bordered-table.table{:class => "table_#{@hex_path}" }
%thead
%th Name
%th Last Update
......@@ -29,34 +29,39 @@
%td
%td
- index = 0
- contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content|
= render :partial => "refs/tree_item", :locals => { :content => content }
= render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) }
- contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content|
= render :partial => "refs/tree_item", :locals => { :content => content }
= render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) }
- contents.select{ |i| i.is_a?(Grit::Submodule)}.each do |content|
= render :partial => "refs/submodule_item", :locals => { :content => content }
= render :partial => "refs/submodule_item", :locals => { :content => content, :index => (index += 1) }
- if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first
#tree-readme-holder
%h3= content.name
.readme
.file_holder#README
.file_title
%i.icon-file
= content.name
.file_content.wiki
- if content.name =~ /\.(md|markdown)$/i
= preserve do
= markdown(content.data)
- else
= simple_format(content.data)
- if params[:path]
- history_path = tree_file_project_ref_path(@project, @ref, params[:path])
- else
- history_path = tree_project_ref_path(@project, @ref)
:javascript
$(function(){
$('select#branch').selectmenu({style:'popup', width:200});
$('select#tag').selectmenu({style:'popup', width:200});
$('.project-refs-select').chosen();
history.pushState({ path: this.path }, '', "#{history_path}")
history.pushState({ path: this.path }, '', "#{@history_path}");
});
// Load last commit log for each file in tree
$(window).load(function(){
ajaxGet('#{@logs_path}');
});
......
- 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
.view_file_header
.file_holder
.file_title
%i.icon-file
%span.file_name
= name
......@@ -10,26 +10,28 @@
= link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
- if file.text?
- if name =~ /\.(md|markdown)$/i
#tree-readme-holder
.readme
= preserve do
= markdown(file.data)
.file_content.wiki
= preserve do
= markdown(file.data)
- else
.view_file_content
.file_content.code
- unless file.empty?
%div{:class => current_user.dark_scheme ? "black" : "white"}
= preserve do
= raw file.colorize(options: { linenos: 'True'})
- else
%h4.nothing_here_message Empty file
- elsif file.image?
.view_file_content_image
.file_content.image_file
%img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else
%center
= link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do
%div.padded
%br
= image_tag "download.png", :width => 64
%h3
Download (#{file.mb_size})
.file_content.blob_file
%center
= link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do
%div.padded
%br
= image_tag "download.png", :width => 64
%h3
Download (#{file.mb_size})
- file = params[:path] ? File.join(params[:path], content.name) : content.name
- content_commit = @project.commits(@commit.id, file, 1).last
- return unless content_commit
%tr{ :class => "tree-item", :url => tree_file_project_ref_path(@project, @ref, file) }
- file = tree_full_path(content)
%tr{ :class => "tree-item #{tree_hex_class(content)}", :url => tree_file_project_ref_path(@project, @ref, file) }
%td.tree-item-file-name
- if content.is_a?(Grit::Blob)
- if content.text?
= image_tag "file_txt.png"
- elsif content.image?
= image_tag "file_img.png"
- else
= image_tag "file_bin.png"
- else
= image_tag "file_dir.png"
= tree_icon(content)
= link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true
%td.cgray
= time_ago_in_words(content_commit.committed_date)
ago
%td.commit
- tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)
- if tm
%strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
= link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
%td.tree_time_ago.cgray
- if index == 1
%span.log_loading
Loading commit data..
= image_tag "ajax_loader_tree.gif", :width => 14
%td.tree_commit
......@@ -11,8 +11,8 @@
%li= link
.clear
.view_file.blame_file
.view_file_header
.file_holder
.file_title
%i.icon-file
%span.file_name
= @tree.name
......@@ -21,7 +21,7 @@
= link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank"
= link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "btn very_small"
= link_to "source", tree_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
.view_file_content
.file_content.blame
%table
- @blame.each do |commit, lines|
- commit = Commit.new(commit)
......@@ -29,7 +29,7 @@
%td.author
= image_tag gravatar_icon(commit.author_email, 16)
= commit.author_name
%td.commit
%td.blame_commit
&nbsp;
= link_to project_commit_path(@project, :id => commit.id) do
%code= commit.id.to_s[0..10]
......@@ -37,8 +37,7 @@
%td.lines
= preserve do
%pre
- lines.each do |line|
= line
= Gitlab::Encode.utf8 lines.join("\n")
:javascript
$(function(){
......
- @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
// Load Files list
$("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}");
$("#tree-content-holder").show("slide", { direction: "right" }, 150);
$('.project-refs-form #path').val("#{params[:path]}");
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}');
});
......@@ -7,16 +7,14 @@
= link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right"
%br
#tree-holder
#tree-content-holder
.view_file
.view_file_header
%i.icon-file
%strong= @snippet.file_name
%span.options
= link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"
.view_file_content
%div{:class => current_user.dark_scheme ? "black" : ""}
= raw @snippet.colorize(options: { linenos: 'True'})
.file_holder
.file_title
%i.icon-file
%strong= @snippet.file_name
%span.options
= link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"
.file_content.code
%div{:class => current_user.dark_scheme ? "black" : ""}
= raw @snippet.colorize(options: { linenos: 'True'})
= render "notes/notes", :tid => @snippet.id, :tt => "snippet"
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
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer
config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer, :system_hook_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
......
......@@ -21,6 +21,8 @@ email:
# Like default project limit for user etc
app:
default_projects_limit: 10
# backup_path: "/vol/backups" # default: Rails.root + backups/
# backup_keep_time: 604800 # default: 0 (forever) (in seconds)
#
......
......@@ -95,11 +95,21 @@ class Settings < Settingslogic
end
def gitolite_admin_uri
git['admin_uri'] || 'git@localhost:gitolite-admin'
git_host['admin_uri'] || 'git@localhost:gitolite-admin'
end
def default_projects_limit
app['default_projects_limit'] || 10
end
def backup_path
t = app['backup_path'] || "backups/"
t = /^\//.match(t) ? t : File.join(Rails.root + t)
t
end
def backup_keep_time
app['backup_keep_time'] || 0
end
end
end
......@@ -93,10 +93,6 @@ Devise.setup do |config|
# If true, extends the user's remember period when remembered via cookie.
# 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
# :secure => true in order to force SSL only cookies.
# config.cookie_options = {}
......@@ -119,7 +115,7 @@ Devise.setup do |config|
# Defines which strategy will be used to lock an account.
# :failed_attempts = Locks an account after a number of failed attempts to sign in.
# :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
# config.unlock_keys = [ :email ]
......@@ -129,14 +125,14 @@ Devise.setup do |config|
# :time = Re-enables login after a certain amount of time (see :unlock_in below)
# :both = Enables both strategies
# :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
# is failed attempts.
# config.maximum_attempts = 20
config.maximum_attempts = 10
# 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
#
......@@ -160,9 +156,9 @@ Devise.setup do |config|
# Defines name of the authentication token params key
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.
config.stateless_token = true
config.skip_session_storage << :token_auth
# ==> Scopes configuration
# Turn scoped views on. Before rendering "sessions/new", it will first check for
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment