Commit 8d74123d authored by Nihad Abbasov's avatar Nihad Abbasov

Merge branch 'master' into features/feeds

parents f476c42d 76294699
v 1.2.0
- new design
- user dashboard
- markdown support for comments
- encoding issues
- wall like twitter timeline
v 1.1.0
- project dashboard
- wall redesigned
......
......@@ -37,7 +37,6 @@ end
group :development, :test do
gem 'rspec-rails'
gem "shoulda", "~> 3.0.0.beta2"
gem 'capybara'
gem 'autotest'
gem 'autotest-rails'
......@@ -51,4 +50,5 @@ end
group :test do
gem 'turn', :require => false
gem 'simplecov', :require => false
gem "shoulda", "~> 3.0.0.beta2"
end
......@@ -10,6 +10,8 @@
//= require jquery.ui.selectmenu
//= require jquery.tagify
//= require jquery.cookie
//= require raphael
//= require branch-graph
//= require_tree .
$(function(){
......@@ -19,8 +21,20 @@ $(function(){
$('select#branch').selectmenu({style:'popup', width:200});
$('select#tag').selectmenu({style:'popup', width:200});
$(".account-box").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu);
});
function updatePage(data){
$.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
}
function showMenu() {
$(this).toggleClass('hover');
}
function resetMenu() {
$(this).removeClass("hover");
}
......@@ -8,3 +8,34 @@
*= require_self
*= require_tree .
*/
/** COLORS **/
.cgray { color:gray; }
.cred { color:#D12F19; }
.cgreen { color:#44aa22; }
/** COMMON STYLES **/
.left {
float:left;
}
.right {
float:right;
}
.width-50p{
width:50%;
}
.width-49p{
width:49%;
}
.width-30p{
width:30%;
}
.width-65p{
width:65%;
}
.append-bottom-10 {
margin-bottom:10px;
}
.prepend-top-10 {
margin-top:10px;
}
/** Commit diff view **/
.diff_file {
border:1px solid #CCC;
margin-bottom:1em;
.diff_file_header {
padding:5px 5px;
border-bottom:1px solid #CCC;
background: #eee;
}
.diff_file_content {
overflow:auto;
overflow-y:hidden;
background:#fff;
color:#333;
font-size: 12px;
font-family: 'Courier New', 'andale mono','lucida console',monospace;
}
.diff_file_content_image {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
}
.diff_file_content{
.old_line, .new_line {
background:#ECECEC;
color:#777;
width:30px;
float:left;
padding: 0px 5px;
border-right: 1px solid #ccc;
}
}
pre.commit_message {
white-space: pre-wrap;
}
// Place all the styles related to the Dashboard controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
// Place all the styles related to the Issues controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.issue-number {
float: left;
border-radius: 5px;
text-shadow: none;
background: rgba(0, 0, 0, 0.12);
text-align: center;
padding: 14px 8px;
width: 40px;
margin-right: 10px;
color: #444;
}
.issues_filter {
margin-top:10px;
.left {
margin-right:15px;
}
}
.top_panel_issues{
#issue_search_form {
margin:5px 0;
input {
border:1px solid #D3D3D3;
padding: 3px;
height: 28px;
width: 300px;
-webkit-appearance:none;
box-sizing: border-box;
-moz-box-sizing: border-box;
&:focus {
border-color:#c2e1ef;
}
}
}
}
/** ISSUES LIST **/
.issue .action-links {
display:none;
a {
margin-left:10px;
}
}
.issue:hover .action-links { display:block; }
/** Notes **/
#notes-list {
display:block;
list-style:none;
margin:0px;
padding:0px;
}
.issue_notes {
.note_content {
float:left;
width:400px;
}
}
/* Note textare */
#note_note {
height:100px;
width:97%;
font-size:14px;
}
#new_note {
#note_note {
height:25px;
}
.attach_holder {
display:none;
}
}
#notes-list .note .delete-note { display:none; }
#notes-list .note:hover .delete-note { display:block; }
body.project-page #notes-list .note {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
body.project-page #notes-list .note {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
body.project-page #notes-list .note img{float: left; margin-right: 10px;}
body.project-page #notes-list .note span.note-title{display: block;}
body.project-page #notes-list .note span.note-title{margin-bottom: 10px}
body.project-page #notes-list .note span.note-author{color: #999; font-weight: normal; font-style: italic;}
body.project-page #notes-list .note span.note-author strong{font-weight: bold; font-style: normal;}
// Place all the styles related to the Profile controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
This diff is collapsed.
// Place all the styles related to the Snippets controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
This diff is collapsed.
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :view_style
protect_from_forgery
helper_method :abilities, :can?
rescue_from Gitosis::AccessDenied do |exception|
......@@ -64,7 +61,7 @@ class ApplicationController < ActionController::Base
else
@branch = params[:branch].blank? ? nil : params[:branch]
@tag = params[:tag].blank? ? nil : params[:tag]
@ref = @branch || @tag || "master"
@ref = @branch || @tag || Repository.default_ref
end
end
......@@ -76,20 +73,6 @@ class ApplicationController < ActionController::Base
redirect_to @project unless @project.repo_exists?
end
def view_style
if params[:view_style] == "collapsed"
cookies[:view_style] = "collapsed"
elsif params[:view_style] == "fluid"
cookies[:view_style] = ""
end
@view_mode = if cookies[:view_style] == "collapsed"
:fixed
else
:fluid
end
end
def respond_with_notes
if params[:last_id] && params[:first_id]
@notes = @notes.where("id >= ?", params[:first_id])
......
......@@ -8,18 +8,18 @@ class CommitsController < ApplicationController
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :require_non_empty_project
before_filter :load_refs, :only => :index # load @branch, @tag & @ref
def index
load_refs # load @branch, @tag & @ref
def index
@repo = project.repo
limit, offset = (params[:limit] || 20), (params[:offset] || 0)
if params[:path]
@commits = @repo.log(@ref, params[:path], :max_count => limit, :skip => offset)
else
@commits = @repo.commits(@ref, limit, offset)
end
@commits = if params[:path]
@repo.log(@ref, params[:path], :max_count => limit, :skip => offset)
else
@repo.commits(@ref, limit, offset)
end
respond_to do |format|
format.html # index.html.erb
......@@ -30,8 +30,8 @@ class CommitsController < ApplicationController
def show
@commit = project.repo.commits(params[:id]).first
@notes = project.notes.where(:noteable_id => @commit.id, :noteable_type => "Commit").order("created_at DESC").limit(20)
@note = @project.notes.new(:noteable_id => @commit.id, :noteable_type => "Commit")
@notes = project.commit_notes(@commit).fresh.limit(20)
@note = @project.build_commit_note(@commit)
respond_to do |format|
format.html
......
require File.join(Rails.root, 'lib', 'graph_commit')
class ProjectsController < ApplicationController
before_filter :project, :except => [:index, :new, :create]
layout :determine_layout
......@@ -6,8 +8,8 @@ class ProjectsController < ApplicationController
before_filter :add_project_abilities
before_filter :authorize_read_project!, :except => [:index, :new, :create]
before_filter :authorize_admin_project!, :only => [:edit, :update, :destroy]
before_filter :require_non_empty_project, :only => [:blob, :tree]
before_filter :load_refs, :only => :tree # load @branch, @tag & @ref
def index
source = current_user.projects
......@@ -64,21 +66,8 @@ class ProjectsController < ApplicationController
def show
return render "projects/empty" unless @project.repo_exists?
@date = case params[:view]
when "week" then Date.today - 7.days
when "day" then Date.today
else nil
end
if @date
@date = @date.at_beginning_of_day
@commits = @project.commits_since(@date)
@messages = project.notes.since(@date).order("created_at DESC")
else
@commits = @project.fresh_commits
@messages = project.notes.fresh.limit(10)
end
limit = (params[:limit] || 40).to_i
@activities = @project.updates(limit)
end
#
......@@ -101,15 +90,13 @@ class ProjectsController < ApplicationController
#
def tree
load_refs # load @branch, @tag & @ref
@repo = project.repo
if params[:commit_id]
@commit = @repo.commits(params[:commit_id]).first
else
@commit = @repo.commits(@ref || "master").first
end
@commit = if params[:commit_id]
@repo.commits(params[:commit_id]).first
else
@repo.commits(@ref).first
end
@tree = @commit.tree
@tree = @tree / params[:path] if params[:path]
......@@ -127,6 +114,34 @@ class ProjectsController < ApplicationController
return render_404
end
def graph
@repo = project.repo
commits = Grit::Commit.find_all(@repo, nil, {:max_count => 650})
ref_cache = {}
commits.collect! do |commit|
add_refs(commit, ref_cache)
GraphCommit.new(commit)
end
days = GraphCommit.index_commits(commits)
@days_json = days.compact.collect{|d| [d.day, d.strftime("%b")] }.to_json
@commits_json = commits.collect do |c|
h = {}
h[:parents] = c.parents.collect do |p|
[p.id,0,0]
end
h[:author] = c.author.name.force_encoding("UTF-8")
h[:time] = c.time
h[:space] = c.space
h[:refs] = c.refs.collect{|r|r.name}.join(" ") unless c.refs.nil?
h[:id] = c.sha
h[:date] = c.date
h[:message] = c.message.force_encoding("UTF-8")
h[:login] = c.author.email
h
end.to_json
end
def blob
@repo = project.repo
@commit = project.commit(params[:commit_id])
......@@ -151,6 +166,17 @@ class ProjectsController < ApplicationController
protected
def add_refs(commit, ref_cache)
if ref_cache.empty?
@repo.refs.each do |ref|
ref_cache[ref.commit.id] ||= []
ref_cache[ref.commit.id] << ref
end
end
commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id
commit.refs ||= []
end
def project
@project ||= Project.find_by_code(params[:id])
end
......
require 'digest/md5'
module ApplicationHelper
def gravatar_icon(user_email)
"http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon"
gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com"
"#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon"
end
def fixed_mode?
@view_mode == :fixed
true
end
def body_class(default_class = nil)
......@@ -13,7 +15,7 @@ module ApplicationHelper
default_class :
content_for(:body_class)
[main, cookies[:view_style]].join(" ")
[main, "collapsed"].join(" ")
end
def commit_name(project, commit)
......
module DashboardHelper
def dashboard_feed_path(project, object)
case object.class.name.to_s
when "Issue" then project_issues_path(project, project.issues.find(object.id))
when "Issue" then project_issue_path(project, project.issues.find(object.id))
when "Grit::Commit" then project_commit_path(project, project.repo.commits(object.id).first)
when "Note"
then
......@@ -19,12 +19,15 @@ module DashboardHelper
end
def dashboard_feed_title(object)
title = case object.class.name.to_s
klass = object.class.to_s.split("::").last
title = case klass
when "Note" then markdown(object.note)
when "Issue" then object.title
when "Grit::Commit" then object.safe_message
when "Commit" then object.safe_message
else return "Project Wall"
end
"[#{object.class.name}] #{truncate(sanitize(title, :tags => []), :length => 60)} "
"[#{klass}] #{truncate(sanitize(title, :tags => []), :length => 60)} "
end
end
module IssuesHelper
def sort_class
if can?(current_user, :admin_issue, @project) && (!params[:f] || params[:f] == "0")
"handle"
end
end
def project_issues_filter_path project, params = {}
params[:f] ||= cookies['issue_filter']
project_issues_path project, params
end
def sort_class
if can?(current_user, :admin_issue, @project) && (!params[:f] || params[:f] == "0")
"handle"
end
end
def project_issues_filter_path project, params = {}
params[:f] ||= cookies['issue_filter']
project_issues_path project, params
end
end
module TagsHelper
def tag_path tag
"/tags/#{tag}"
end
def tag_path tag
"/tags/#{tag}"
end
def tag_list project
html = ''
project.tag_list.each do |tag|
html += link_to tag, tag_path(tag)
end
html.html_safe
end
def tag_list project
html = ''
project.tag_list.each do |tag|
html += link_to tag, tag_path(tag)
end
html.html_safe
end
end
......@@ -54,5 +54,6 @@ end
# updated_at :datetime
# closed :boolean default(FALSE), not null
# position :integer default(0)
# critical :boolean default(FALSE), not null
#
......@@ -38,7 +38,7 @@ end
# Table name: notes
#
# id :integer not null, primary key
# note :string(255)
# note :text
# noteable_id :string(255)
# noteable_type :string(255)
# author_id :integer
......
......@@ -46,6 +46,25 @@ class Project < ActiveRecord::Base
scope :public_only, where(:private_flag => false)
def repository
@repository ||= Repository.new(self)
end
delegate :repo,
:url_to_repo,
:path_to_repo,
:update_gitosis_project,
:destroy_gitosis_project,
:tags,
:repo_exists?,
:commit,
:commits,
:tree,
:heads,
:commits_since,
:fresh_commits,
:to => :repository, :prefix => nil
def to_param
code
end
......@@ -59,16 +78,12 @@ class Project < ActiveRecord::Base
notes.where(:noteable_type => ["", nil])
end
def update_gitosis_project
Gitosis.new.configure do |c|
c.update_project(path, gitosis_writers)
end
def build_commit_note(commit)
notes.new(:noteable_id => commit.id, :noteable_type => "Commit")
end
def destroy_gitosis_project
Gitosis.new.configure do |c|
c.destroy_project(self)
end
def commit_notes(commit)
notes.where(:noteable_id => commit.id, :noteable_type => "Commit")
end
def add_access(user, *access)
......@@ -106,26 +121,6 @@ class Project < ActiveRecord::Base
private_flag
end
def url_to_repo
"#{GITOSIS["git_user"]}@#{GITOSIS["host"]}:#{path}.git"
end
def path_to_repo
GITOSIS["base_path"] + path + ".git"
end
def repo
@repo ||= Grit::Repo.new(path_to_repo)
end
def tags
repo.tags.map(&:name).sort.reverse
end
def repo_exists?
repo rescue false
end
def last_activity
updates(1).first
rescue
......@@ -146,48 +141,6 @@ class Project < ActiveRecord::Base
end[0...n]
end
def commit(commit_id = nil)
if commit_id
repo.commits(commit_id).first
else
repo.commits.first
end
end
def heads
@heads ||= repo.heads
end
def fresh_commits(n = 10)
commits = heads.map do |h|
repo.commits(h.name, n)
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0...n]
end
def commits_since(date)
commits = heads.map do |h|
repo.log(h.name, nil, :since => date)
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
path ? (tree / path) : tree
end
def check_limit
unless owner.can_create_project?
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
......
class Repository
attr_accessor :project
def self.default_ref
"master"
end
def initialize(project)
@project = project
end
def path
@path ||= project.path
end
def project_id
project.id
end
def repo
@repo ||= Grit::Repo.new(project.path_to_repo)
end
def url_to_repo