diff --git a/CHANGELOG b/CHANGELOG index 1201a857..96acfba1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,32 @@ +v 1.1.0 + - project dashboard + - wall redesigned + - feature: code snippets + - fixed horizontal scroll on file preview + - fixed app crash if commit message has invalid chars + - bugfix & code cleaning + +v 1.0.2 + - fixed bug with empty project + - added adv validation for project path & code + - feature: issues can be sortable + - bugfix + - username dispalyed on top panel + v 1.0.1 - fixed: with invalid source code for commit - fixed: lose branch/tag selection when use tree navigateion - when history clicked - display path - bug fix & code cleaning + v 1.0.0 - bug fix - projects preview mode + v 0.9.6 - css fix - new repo empty tree until restart server - fixed + v 0.9.4 - security improved - authorization improved @@ -24,6 +42,7 @@ v 0.9.1 - updated app name - issue redesigned - issue can be edit + v 0.8.0 - sytax highlight for main file types - redesign diff --git a/Gemfile b/Gemfile index 523793e7..77a00659 100644 --- a/Gemfile +++ b/Gemfile @@ -15,7 +15,7 @@ gem 'therubyracer' gem 'faker' gem 'seed-fu', :git => 'git://github.com/mbleigh/seed-fu.git' gem "inifile" -gem "albino", :git => "git://github.com/gitlabhq/albino.git" +gem "pygments.rb", "0.2.3" gem "kaminari" gem "thin" gem "git" diff --git a/Gemfile.lock b/Gemfile.lock index 2aa894aa..5ba34459 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,13 +4,6 @@ GIT specs: annotate (2.4.1.beta1) -GIT - remote: git://github.com/gitlabhq/albino.git - revision: 118380924969f3a856659f86ea1f40c1ba7bfcb1 - specs: - albino (1.3.3) - posix-spawn (>= 0.3.6) - GIT remote: git://github.com/gitlabhq/grit.git revision: ff015074ef35bd94cba943f9c0f98e161ab5851c @@ -72,6 +65,7 @@ GEM ZenTest (= 4.5) awesome_print (0.4.0) bcrypt-ruby (3.0.1) + blankslate (2.1.2.4) builder (3.0.0) capybara (1.0.1) mime-types (>= 1.16) @@ -138,6 +132,8 @@ GEM orm_adapter (0.0.5) polyglot (0.3.2) posix-spawn (0.3.6) + pygments.rb (0.2.3) + rubypython (>= 0.5.1) rack (1.3.2) rack-cache (1.0.3) rack (>= 0.4) @@ -189,6 +185,9 @@ GEM ruby-debug-base19 (>= 0.11.19) ruby_core_source (0.1.5) archive-tar-minitar (>= 0.5.2) + rubypython (0.5.1) + blankslate (>= 2.1.2.3) + ffi (~> 1.0.7) rubyzip (0.9.4) sass (3.1.7) sass-rails (3.1.1) @@ -242,7 +241,6 @@ PLATFORMS DEPENDENCIES acts_as_list - albino! annotate! autotest autotest-rails @@ -260,6 +258,7 @@ DEPENDENCIES jquery-rails kaminari launchy + pygments.rb (= 0.2.3) rails (= 3.1.0) rails-footnotes (>= 3.7.5.rc4) rspec-rails diff --git a/VERSION b/VERSION index 7dea76ed..9084fa2f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.1 +1.1.0 diff --git a/app/assets/images/home.png b/app/assets/images/home.png new file mode 100644 index 00000000..316d5d42 Binary files /dev/null and b/app/assets/images/home.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 01e3b416..024dfe11 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -16,3 +16,7 @@ $(function(){ $('select#branch').selectmenu({style:'popup', width:200}); $('select#tag').selectmenu({style:'popup', width:200}); }); + +function updatePage(){ + $.ajax({type: "GET", url: location.href, dataType: "script"}); +} diff --git a/app/assets/javascripts/snippets.js b/app/assets/javascripts/snippets.js new file mode 100644 index 00000000..11e18eb7 --- /dev/null +++ b/app/assets/javascripts/snippets.js @@ -0,0 +1,9 @@ +$(document).ready(function(){ + $("#snippets-table .snippet").live('click', function(e){ + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") { + location.href = $(this).attr("url"); + e.stopPropagation(); + return false; + } + }); +}); diff --git a/app/assets/stylesheets/highlight.css.scss b/app/assets/stylesheets/highlight.css.scss index 05cb98e7..31f9369a 100644 --- a/app/assets/stylesheets/highlight.css.scss +++ b/app/assets/stylesheets/highlight.css.scss @@ -22,8 +22,8 @@ td.linenos{ .highlight{ background:none; - padding:10px 0px 0px 0; - margin-left:10px; + padding:10px 0px 0px 10px; + margin-left:0px; } .highlight pre{ } @@ -43,7 +43,7 @@ td.linenos { } td.code .highlight { - overflow-x: scroll; + overflow: auto; } table.highlighttable pre{ padding:0; diff --git a/app/assets/stylesheets/projects.css.scss b/app/assets/stylesheets/projects.css.scss index bc15d8e2..51788672 100644 --- a/app/assets/stylesheets/projects.css.scss +++ b/app/assets/stylesheets/projects.css.scss @@ -310,6 +310,7 @@ input.ssh_project_url { } #projects-list .project, +#snippets-table .snippet, #issues-table .issue{ cursor:pointer; @@ -360,6 +361,8 @@ input.ssh_project_url { .user_new, .edit_user, .new_project, +.new_snippet, +.edit_snippet, .edit_project { input[type='text'], input[type='email'], @@ -488,8 +491,14 @@ tbody tr:nth-child(2n) td, tbody tr.even td { background: white; } p { - margin-bottom: 3px; - font-size: 12px; + margin-bottom: 4px; + font-size: 13px; + color:#111; + } + } + cite { + &.ago { + color:#666; } } } @@ -512,7 +521,7 @@ tbody tr:nth-child(2n) td, tbody tr.even td { } .note_content { float:left; - width:750px; + width:650px; } .issue_notes { @@ -549,3 +558,83 @@ tbody tr:nth-child(2n) td, tbody tr.even td { height: 12px; padding: 10px; } + +.recent_message_parent { + img { + padding-right:10px; + } + + float: left; + margin: 0 20px 20px 0px; + padding: 5px 0px;; + width: 420px; + + &.dash_wall{ + border-bottom: 2px solid orange; + span { + background: orange; + color:black; + } + } + + &.dash_issue{ + border-bottom: 2px solid #ffbbbb; + span { + background: #ffbbbb; + } + } + &.dash_commit{ + border-bottom: 2px solid #bbbbff; + + span{ + background: #bbbbff; + } + } + + &.dash_snippet{ + border-bottom: 2px solid #bbffbb; + + span{ + background: #bbffbb; + } + } + + + span{ + border: 1px solid #aaa; + color:black; + padding:1px 4px; + } + + h4 { + margin-bottom:3px; + } + +} +.commit, +.message, +#notes-list{ + .author { + background: #eaeaea; + color: #333; + border: 1px solid #aaa; + padding:1px 2px; + margin-right:5px; + } +} + +/* Note textare */ +#note_note { + height:100px; + width:97%; + font-size:14px; +} + +.wall_page { + #note_note { + height:25px; + } + .attach_holder { + display:none; + } +} diff --git a/app/assets/stylesheets/snippets.css.scss b/app/assets/stylesheets/snippets.css.scss new file mode 100644 index 00000000..1b680d87 --- /dev/null +++ b/app/assets/stylesheets/snippets.css.scss @@ -0,0 +1,3 @@ +// 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/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2866d1a7..047c6cb7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -27,11 +27,15 @@ class ApplicationController < ActionController::Base end def authenticate_admin! - return redirect_to(new_user_session_path) unless current_user.is_admin? + return render_404 unless current_user.is_admin? end def authorize_project!(action) - return redirect_to(new_user_session_path) unless can?(current_user, action, project) + return render_404 unless can?(current_user, action, project) + end + + def access_denied! + render_404 end def method_missing(method_sym, *arguments, &block) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 6da5dea8..cf8e1ebe 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,12 +1,12 @@ class IssuesController < ApplicationController before_filter :authenticate_user! before_filter :project + before_filter :issue, :only => [:edit, :update, :destroy, :show] # Authorize before_filter :add_project_abilities before_filter :authorize_read_issue! before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update, :sort] - before_filter :authorize_admin_issue!, :only => [:destroy] respond_to :js @@ -30,12 +30,10 @@ class IssuesController < ApplicationController end def edit - @issue = @project.issues.find(params[:id]) respond_with(@issue) end def show - @issue = @project.issues.find(params[:id]) @notes = @issue.notes @note = @project.notes.new(:noteable => @issue) end @@ -51,7 +49,6 @@ class IssuesController < ApplicationController end def update - @issue = @project.issues.find(params[:id]) @issue.update_attributes(params[:issue]) respond_to do |format| @@ -62,7 +59,8 @@ class IssuesController < ApplicationController def destroy - @issue = @project.issues.find(params[:id]) + return access_denied! unless can?(current_user, :admin_issue, @issue) + @issue.destroy respond_to do |format| @@ -79,4 +77,10 @@ class IssuesController < ApplicationController render :nothing => true end + + protected + + def issue + @issue ||= @project.issues.find(params[:id]) + end end diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index d0a40eb1..46425664 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -4,7 +4,6 @@ class NotesController < ApplicationController # Authorize before_filter :add_project_abilities before_filter :authorize_write_note!, :only => [:create] - before_filter :authorize_admin_note!, :only => [:destroy] respond_to :js @@ -25,6 +24,9 @@ class NotesController < ApplicationController def destroy @note = @project.notes.find(params[:id]) + + return access_denied! unless can?(current_user, :admin_note, @note) + @note.destroy respond_to do |format| @@ -41,6 +43,8 @@ class NotesController < ApplicationController Notify.note_commit_email(u, @note).deliver when "Issue" then Notify.note_issue_email(u, @note).deliver + when "Snippet" + true else Notify.note_wall_email(u, @note).deliver end diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb index 666c6309..c8477729 100644 --- a/app/controllers/profile_controller.rb +++ b/app/controllers/profile_controller.rb @@ -3,6 +3,12 @@ class ProfileController < ApplicationController @user = current_user end + def social_update + @user = current_user + @user.update_attributes(params[:user]) + redirect_to [:profile] + end + def password @user = current_user end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e8f1c392..54d19af7 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -60,14 +60,21 @@ class ProjectsController < ApplicationController end def show - @repo = project.repo - @commit = @repo.commits.first - @tree = @commit.tree - @tree = @tree / params[:path] if params[:path] + 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 - rescue Grit::NoSuchPathError => ex - respond_to do |format| - format.html {render "projects/empty"} + 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 end @@ -76,8 +83,15 @@ class ProjectsController < ApplicationController # def wall - @notes = @project.common_notes @note = Note.new + @notes = @project.common_notes.order("created_at DESC") + + @notes = case params[:view] + when "week" then @notes.since((Date.today - 7.days).at_beginning_of_day) + when "all" then @notes.all + when "day" then @notes.since(Date.today.at_beginning_of_day) + else @notes.fresh.limit(10) + end end # diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb new file mode 100644 index 00000000..b31fe683 --- /dev/null +++ b/app/controllers/snippets_controller.rb @@ -0,0 +1,62 @@ +class SnippetsController < ApplicationController + before_filter :authenticate_user! + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_snippet! + before_filter :authorize_write_snippet!, :only => [:new, :create, :close, :edit, :update, :sort] + + respond_to :html + + def index + @snippets = @project.snippets + end + + def new + @snippet = @project.snippets.new + end + + def create + @snippet = @project.snippets.new(params[:snippet]) + @snippet.author = current_user + @snippet.save + + if @snippet.valid? + redirect_to [@project, @snippet] + else + respond_with(@snippet) + end + end + + def edit + @snippet = @project.snippets.find(params[:id]) + end + + def update + @snippet = @project.snippets.find(params[:id]) + @snippet.update_attributes(params[:snippet]) + + if @snippet.valid? + redirect_to [@project, @snippet] + else + respond_with(@snippet) + end + end + + def show + @snippet = @project.snippets.find(params[:id]) + @notes = @snippet.notes + @note = @project.notes.new(:noteable => @snippet) + end + + def destroy + @snippet = @project.snippets.find(params[:id]) + + return access_denied! unless can?(current_user, :admin_snippet, @snippet) + + @snippet.destroy + + redirect_to project_snippets_path(@project) + end +end diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb index e00cc36c..5fb2710d 100644 --- a/app/controllers/team_members_controller.rb +++ b/app/controllers/team_members_controller.rb @@ -3,8 +3,8 @@ class TeamMembersController < ApplicationController # Authorize before_filter :add_project_abilities - before_filter :authorize_read_team_member! - before_filter :authorize_admin_team_member!, :only => [:new, :create, :destroy, :update] + before_filter :authorize_read_project! + before_filter :authorize_admin_project!, :only => [:new, :create, :destroy, :update] def show @team_member = project.users_projects.find(params[:id]) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 89a906f0..0895eb0d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -53,25 +53,4 @@ module ApplicationHelper [projects, default_nav, project_nav].flatten.to_json end - def handle_file_type(file_name, mime_type) - if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/ - :ruby - elsif file_name =~ /\.py$/ - :python - elsif file_name =~ /(\.pl|\.scala|\.c|\.cpp|\.java|\.haml|\.html|\.sass|\.scss|\.xml|\.php|\.erb)$/ - $1[1..-1].to_sym - elsif file_name =~ /\.js$/ - :javascript - elsif file_name =~ /\.sh$/ - :bash - elsif file_name =~ /\.coffee$/ - :coffeescript - elsif file_name =~ /\.yml$/ - :yaml - elsif file_name =~ /\.md$/ - :minid - else - :text - end - end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f1b54668..b79e5718 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -21,4 +21,13 @@ module CommitsHelper link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit), :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link" end + + # Cause some errors with trucate & encoding use this method + def truncate_commit_message(commit, size = 60) + message = commit.message + message.length > size ? (message[0..(size - 1)] + "...") : message + # if special characters occurs + rescue + commit.message + end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d570dff4..4a6923af 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -3,4 +3,16 @@ module ProjectsHelper cookies["project_view"] ||= "tile" cookies["project_view"] == type ? nil : "display:none" end + + def load_note_parent(id, type, project) + case type + when "Issue" then @project.issues.find(id) + when "Commit" then @project.repo.commits(id).first + when "Snippet" then @project.snippets.find(id) + else + true + end + rescue + nil + end end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb new file mode 100644 index 00000000..236b6c8c --- /dev/null +++ b/app/helpers/snippets_helper.rb @@ -0,0 +1,2 @@ +module SnippetsHelper +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 0a2c45f1..b822f630 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,6 +2,9 @@ class Ability def self.allowed(object, subject) case subject.class.name when "Project" then project_abilities(object, subject) + when "Issue" then issue_abilities(object, subject) + when "Note" then note_abilities(object, subject) + when "Snippet" then snippet_abilities(object, subject) else [] end end @@ -12,6 +15,7 @@ class Ability rules << [ :read_project, :read_issue, + :read_snippet, :read_team_member, :read_note ] if project.readers.include?(user) @@ -19,16 +23,35 @@ class Ability rules << [ :write_project, :write_issue, + :write_snippet, :write_note ] if project.writers.include?(user) rules << [ :admin_project, :admin_issue, + :admin_snippet, :admin_team_member, :admin_note ] if project.admins.include?(user) rules.flatten end + + class << self + [:issue, :note, :snippet].each do |name| + define_method "#{name}_abilities" do |user, subject| + if subject.author == user + [ + :"read_#{name}", + :"write_#{name}", + :"admin_#{name}" + ] + else + subject.respond_to?(:project) ? + project_abilities(user, subject.project) : [] + end + end + end + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 9b1b923e..556cdc1c 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -37,5 +37,6 @@ end # created_at :datetime # updated_at :datetime # closed :boolean default(FALSE), not null +# position :integer default(0) # diff --git a/app/models/note.rb b/app/models/note.rb index f89fb9f8..e3dabce4 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -22,6 +22,10 @@ class Note < ActiveRecord::Base scope :common, where(:noteable_id => nil) + scope :last_week, where("created_at >= :date", :date => (Date.today - 7.days)) + scope :since, lambda { |day| where("created_at >= :date", :date => (day)) } + scope :fresh, order("created_at DESC") + mount_uploader :attachment, AttachmentUploader end # == Schema Information diff --git a/app/models/project.rb b/app/models/project.rb index 2340940b..d70b18e7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -7,6 +7,7 @@ class Project < ActiveRecord::Base has_many :users_projects, :dependent => :destroy has_many :users, :through => :users_projects has_many :notes, :dependent => :destroy + has_many :snippets, :dependent => :destroy validates :name, :uniqueness => true, @@ -125,6 +126,34 @@ class Project < ActiveRecord::Base end end + def heads + @heads ||= repo.heads + end + + def fresh_commits + commits = heads.map do |h| + repo.commits(h.name, 10) + end.flatten.uniq { |c| c.id } + + commits.sort! do |x, y| + y.committed_date <=> x.committed_date + end + + commits[0..10] + 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 diff --git a/app/models/snippet.rb b/app/models/snippet.rb new file mode 100644 index 00000000..95d6a07d --- /dev/null +++ b/app/models/snippet.rb @@ -0,0 +1,51 @@ +class Snippet < ActiveRecord::Base + include Utils::Colorize + + belongs_to :project + belongs_to :author, :class_name => "User" + has_many :notes, :as => :noteable + + attr_protected :author, :author_id, :project, :project_id + + validates_presence_of :project_id + validates_presence_of :author_id + + validates :title, + :presence => true, + :length => { :within => 0..255 } + + validates :file_name, + :presence => true, + :length => { :within => 0..255 } + + validates :content, + :presence => true, + :length => { :within => 0..10000 } + + + def self.content_types + [ + ".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java", + ".haml", ".html", ".sass", ".scss", ".xml", ".php", ".erb", + ".js", ".sh", ".coffee", ".yml", ".md" + ] + end + + def colorize + system_colorize(content, file_name) + end +end +# == Schema Information +# +# Table name: snippets +# +# id :integer not null, primary key +# title :string(255) +# content :text +# author_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# file_name :string(255) +# + diff --git a/app/models/user.rb b/app/models/user.rb index 0972f006..7736599e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,7 +5,8 @@ class User < ActiveRecord::Base :recoverable, :rememberable, :trackable, :validatable # Setup accessible (or protected) attributes for your model - attr_accessible :email, :password, :password_confirmation, :remember_me, :name, :projects_limit + attr_accessible :email, :password, :password_confirmation, :remember_me, + :name, :projects_limit, :skype, :linkedin, :twitter has_many :users_projects, :dependent => :destroy has_many :projects, :through => :users_projects @@ -58,5 +59,8 @@ end # name :string(255) # admin :boolean default(FALSE), not null # projects_limit :integer +# skype :string +# linkedin :string +# twitter :string # diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 17be416f..aa9df298 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -25,13 +25,26 @@ = f.label :password_confirmation %br = f.password_field :password_confirmation - .span-11 - .field.prepend-top.append-bottom + .field.prepend-top = f.check_box :admin = f.label :admin + .span-11 .field.prepend-top = f.text_field :projects_limit, :class => "small_input" = f.label :projects_limit + + .field + = f.label :skype + %br + = f.text_field :skype + .field + = f.label :linkedin + %br + = f.text_field :linkedin + .field + = f.label :twitter + %br + = f.text_field :twitter .clear %br .actions diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index b1d110be..aee73c38 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -14,6 +14,17 @@ %b Projects limit: = @admin_user.projects_limit + %p + %b Skype: + = @admin_user.skype + %p + %b LinkedIn: + = @admin_user.linkedin + %p + %b Twitter: + = @admin_user.twitter + + .clear = link_to 'Edit', edit_admin_user_path(@admin_user) \| diff --git a/app/views/commits/_commits.html.haml b/app/views/commits/_commits.html.haml index 4eebb83a..94a1bd1b 100644 --- a/app/views/commits/_commits.html.haml +++ b/app/views/commits/_commits.html.haml @@ -11,12 +11,12 @@ = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" %p %strong - = commit.message.length > 60 ? (commit.message[0..59] + "...") : commit.message + = truncate_commit_message(commit) = link_to "Browse Code", tree_project_path(@project, :commit_id => commit.id), :class => "lite_button", :style => "float:right" = link_to truncate(commit.id.to_s, :length => 16), project_commit_path(@project, :id => commit.id), :class => "lite_button", :style => "width:120px;float:right" %span - %span - [ #{commit.author} ] + %span.author + = commit.author = time_ago_in_words(commit.committed_date) ago = more_commits_link if @commits.size > 99 diff --git a/app/views/commits/_diff.html.haml b/app/views/commits/_diff.html.haml index dff99bf1..73652aaf 100644 --- a/app/views/commits/_diff.html.haml +++ b/app/views/commits/_diff.html.haml @@ -1,4 +1,3 @@ -- require "utils" .file_stats - @commit.diffs.each do |diff| - if diff.deleted_file @@ -35,7 +34,7 @@ %strong{:id => "#{diff.b_path}"}= diff.b_path %br/ .diff_file_content - - if file.mime_type =~ /application|text/ && !Utils.binary?(file.data) + - if file.text? - lines_arr = diff.diff.lines.to_a - line_old = lines_arr[2].match(/-(\d)/)[0].to_i.abs rescue 0 - line_new = lines_arr[2].match(/\+(\d)/)[0].to_i.abs rescue 0 @@ -50,9 +49,9 @@ - else - line_new += 1 - line_old += 1 - - elsif file.mime_type =~ /image/ + - elsif file.image? .diff_file_content_image - %img{:src => "data:image/jpeg;base64,#{Base64.encode64(file.data)}"} + %img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - else %p %center No preview for this file type diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml index 147aaafb..a2c9149d 100644 --- a/app/views/commits/show.html.haml +++ b/app/views/commits/show.html.haml @@ -1,5 +1,5 @@ %h3 - = "[ #{@commit.committer} ] #{truncate @commit.message, :length => 80}" + = "[ #{@commit.committer} ] #{truncate_commit_message(@commit, 80)}" -#= link_to 'Back', project_commits_path(@project), :class => "button" %table.round-borders %tr diff --git a/app/views/commits/show.js.haml b/app/views/commits/show.js.haml index 2c46689b..cec1fe28 100644 --- a/app/views/commits/show.js.haml +++ b/app/views/commits/show.js.haml @@ -1,6 +1,8 @@ -:plain +-#:plain $("#side-commit-preview").remove(); var side = $("
"); side.html("#{escape_javascript(render "commits/show")}"); $("##{dom_id(@project)}").parent().append(side); $("##{dom_id(@project)}").addClass("span-14"); +:plain + $("#notes-list").html("#{escape_javascript(render(:partial => 'notes/notes_list'))}"); diff --git a/app/views/issues/show.js.haml b/app/views/issues/show.js.haml new file mode 100644 index 00000000..5b9a34c0 --- /dev/null +++ b/app/views/issues/show.js.haml @@ -0,0 +1,2 @@ +:plain + $("#notes-list").html("#{escape_javascript(render(:partial => 'notes/notes_list'))}"); diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_form.html.haml index ccb159f1..ca56a245 100644 --- a/app/views/notes/_form.html.haml +++ b/app/views/notes/_form.html.haml @@ -12,9 +12,9 @@ = f.label :note %cite (255 symbols only) %br - = f.text_area :note, :style => "width:97%;height:100px", :size => 255 + = f.text_area :note, :size => 255 - %div + %div.attach_holder = f.label :attachment %cite (less than 10 MB) %br @@ -25,4 +25,4 @@ .clear %br - = f.submit 'Add note', :class => "lbutton vm" + = f.submit 'Add note', :class => "lbutton vm", :id => "submit_note" diff --git a/app/views/notes/_notes.html.haml b/app/views/notes/_notes.html.haml index 9d1d4b94..2d110162 100644 --- a/app/views/notes/_notes.html.haml +++ b/app/views/notes/_notes.html.haml @@ -1,15 +1,28 @@ -%ul#notes-list - - @notes.each do |note| - - next unless note.author - = render :partial => "notes/show", :locals => {:note => note} +- if controller.action_name == "wall" + %ul#notes-list= render "notes/notes_list" -%br -%br -- if can? current_user, :write_note, @project - = render "notes/form" +- else + %ul#notes-list= render "notes/notes_list" + %br + %br + - if can? current_user, :write_note, @project + = render "notes/form" :javascript $('.delete-note').live('ajax:success', function() { $(this).closest('li').fadeOut(); }); + $("#new_note").live("ajax:before", function(){ + $("#submit_note").attr("disabled", "disabled"); + }) + $("#new_note").live("ajax:complete", function(){ + $("#submit_note").removeAttr("disabled"); + }) + + +- if ["issues", "projects"].include?(controller.controller_name) + :javascript + $(function(){ + var int =self.setInterval("updatePage()", 20000); + }); diff --git a/app/views/notes/_notes_list.html.haml b/app/views/notes/_notes_list.html.haml new file mode 100644 index 00000000..1e4a6bb2 --- /dev/null +++ b/app/views/notes/_notes_list.html.haml @@ -0,0 +1,4 @@ +- @notes.each do |note| + - next unless note.author + = render :partial => "notes/show", :locals => {:note => note} + diff --git a/app/views/notes/_show.html.haml b/app/views/notes/_show.html.haml index 2b0a6d2e..ee9f9ffa 100644 --- a/app/views/notes/_show.html.haml +++ b/app/views/notes/_show.html.haml @@ -1,19 +1,17 @@ %li{:id => dom_id(note)} %div.note_author = image_tag gravatar_icon(note.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" - %div.note_content + %div.note_content.left = simple_format(html_escape(note.note)) - if note.attachment.url Attachment: - = link_to note.attachment_identifier, note.attachment.url + = link_to note.attachment_identifier, note.attachment.url, :target => "_blank" %br - %span - %span - [ #{note.author.name} ] -   + %span.author= note.author.name + %cite.ago = time_ago_in_words(note.updated_at) ago - %br + %br - if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project) = link_to 'Remove', [@project, note], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-note right negative" .clear diff --git a/app/views/notes/create.js.haml b/app/views/notes/create.js.haml index 47cff1d8..15371dbc 100644 --- a/app/views/notes/create.js.haml +++ b/app/views/notes/create.js.haml @@ -1,8 +1,11 @@ - if @note.valid? :plain $("#new_note .errors").remove(); - $("#notes-list").append("#{escape_javascript(render(:partial => 'show', :locals => {:note => @note} ))}"); + updatePage(); $('#note_note').val(""); - else :plain $("#new_note").replaceWith("#{escape_javascript(render('form'))}"); + +:plain + $("#submit_note").removeAttr("disabled"); diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml index 12737ba8..ef23a169 100644 --- a/app/views/profile/show.html.haml +++ b/app/views/profile/show.html.haml @@ -6,3 +6,28 @@ %p %b Email: = @user.email + +%br + += form_for @user, :url => profile_edit_path, :method => :put do |f| + -if @user.errors.any? + #error_explanation + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .div + = f.label :skype + %br + = f.text_field :skype + .div + = f.label :linkedin + %br + = f.text_field :linkedin + .div + = f.label :twitter + %br + = f.text_field :twitter + .actions + = f.submit 'Save', :class => "lbutton vm" + diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index baa1f14f..00ca98ef 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -1,8 +1,6 @@ = form_for(@project, :remote => true) do |f| %div.form_content - - if @project.new_record? - %h1 New Project - - else + - unless @project.new_record? %h1 Edit Project - if @project.errors.any? #error_explanation @@ -26,7 +24,7 @@ %td .left= f.label :code %cite.right http://yourserver/ - %td= f.text_field :code, :placeholder => "example (3..12 symbols only)" + %td= f.text_field :code, :placeholder => "example" .field = f.label :description %br/ diff --git a/app/views/projects/_recent_commits.html.haml b/app/views/projects/_recent_commits.html.haml new file mode 100644 index 00000000..e435ea30 --- /dev/null +++ b/app/views/projects/_recent_commits.html.haml @@ -0,0 +1,18 @@ +- @commits.each do |commit| + %div.commit + - if commit.author.email + = image_tag gravatar_icon(commit.author.email), :class => "left", :width => 40, :style => "padding-right:5px;" + - else + = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;" + %p{:style => "margin-bottom: 3px;"} + %strong + = link_to truncate_commit_message(commit, 60), project_commit_path(@project, :id => commit.id) + + %span + %span.author + = commit.author.name.force_encoding("UTF-8") + %cite + = time_ago_in_words(commit.committed_date) + ago + %br + diff --git a/app/views/projects/_recent_messages.html.haml b/app/views/projects/_recent_messages.html.haml new file mode 100644 index 00000000..1af7fe3b --- /dev/null +++ b/app/views/projects/_recent_messages.html.haml @@ -0,0 +1,52 @@ +- @messages.group_by{ |x| [x.noteable_id, x.noteable_type]}.each do |item, notes| + - id, type = item[0], item[1] + - parent = load_note_parent(id, type, @project) + - next unless parent + + - case type + - when "Issue" + - css_class = "dash_issue" + - issue = parent + - item_code = issue.author.email + - link_item_name = truncate(issue.title, :length => 50) + - link_to_item = project_issue_path(@project, issue) + - when "Snippet" + - css_class = "dash_snippet" + - item_code = parent.author.email + - link_item_name = parent.title + - link_to_item = project_snippet_path(@project, parent) + - when "Commit" + - css_class = "dash_commit" + - commit = parent + - item_code = commit.author.email + - link_item_name = truncate_commit_message(commit, 50) + - link_to_item = project_commit_path(@project, :id => commit.id) + - else + - css_class = "dash_wall" + - item_code = @project.name + - link_item_name = "Project Wall" + - link_to_item = wall_project_path(@project) + + %div{ :class => "recent_message_parent #{css_class}"} + = image_tag gravatar_icon(item_code), :class => "left", :width => 40 + %h4 + = link_to(link_item_name, link_to_item) + %span + = type + .clear + - notes.sort {|x,y| x.updated_at <=> y.updated_at }.each do |note| + %div.message + = image_tag gravatar_icon(note.author.email), :class => "left", :width => 24, :style => "padding-right:5px;" + %p{:style => "margin-bottom: 3px;"} + %span.author + = note.author.name + = link_to truncate(note.note, :length => 200), link_to_item + "#note_#{note.id}" + - if note.attachment.url + %br + Attachment: + = link_to note.attachment_identifier, note.attachment.url + %br + %br + .append-bottom +   + .clear diff --git a/app/views/projects/_tile.html.haml b/app/views/projects/_tile.html.haml index 037aeccb..b2466d93 100644 --- a/app/views/projects/_tile.html.haml +++ b/app/views/projects/_tile.html.haml @@ -4,7 +4,7 @@ %div{ :class => "project", :url => project_path(project) } %h2 = image_tag gravatar_icon(project.name), :class => "left", :width => 40, :style => "padding-right:5px;" - = "/" + project.code + = link_to ("/" + project.code), project_path(project), :style => "text-decoration:none" %p= project.name %p= project.url_to_repo -#%p diff --git a/app/views/projects/_top_menu.html.haml b/app/views/projects/_top_menu.html.haml index b81ba6bb..59f2533e 100644 --- a/app/views/projects/_top_menu.html.haml +++ b/app/views/projects/_top_menu.html.haml @@ -1,10 +1,11 @@ %div.top_project_menu -#%span= link_to @project.code.capitalize, @project, :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil - if @project.repo_exists? - %span= link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) || current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil + %span= link_to image_tag("home.png", :width => 20), project_path(@project), :class => current_page?(:controller => "projects", :action => "show", :id => @project) ? "current" : nil + %span= link_to "Tree", tree_project_path(@project), :class => current_page?(:controller => "projects", :action => "tree", :id => @project) ? "current" : nil %span= link_to "Commits", project_commits_path(@project), :class => current_page?(:controller => "commits", :action => "index", :project_id => @project) ? "current" : nil %span - = link_to team_project_path(@project), :class => current_page?(:controller => "projects", :action => "team", :id => @project) ? "current" : nil do + = link_to team_project_path(@project), :class => (current_page?(:controller => "projects", :action => "team", :id => @project) || controller.controller_name == "team_members") ? "current" : nil do Team - if @project.users_projects.count > 0 %span{ :class => "top_menu_count" }= @project.users_projects.count @@ -18,6 +19,11 @@ Wall - if @project.common_notes.count > 0 %span{ :class => "top_menu_count" }= @project.common_notes.count + %span + = link_to project_snippets_path(@project), :class => (controller.controller_name == "snippets") ? "current" : nil do + Snippets + - if @project.snippets.count > 0 + %span{ :class => "top_menu_count" }= @project.snippets.count - if @commit %span= link_to truncate(commit_name(@project,@commit), :length => 15), project_commit_path(@project, :id => @commit.id), :class => current_page?(:controller => "commits", :action => "show", :project_id => @project, :id => @commit.id) ? "current" : nil diff --git a/app/views/projects/_tree_file.html.haml b/app/views/projects/_tree_file.html.haml index 3463bfc5..41a2287a 100644 --- a/app/views/projects/_tree_file.html.haml +++ b/app/views/projects/_tree_file.html.haml @@ -1,4 +1,4 @@ -- require "utils" +:css .view_file .view_file_header %strong @@ -6,14 +6,13 @@ = link_to "raw", blob_project_path(@project, :commit_id => @commit.id, :path => params[:path] ), :class => "right", :target => "_blank" = link_to "history", project_commits_path(@project, :path => params[:path]), :class => "right", :style => "margin-right:10px;" %br/ - - if file.mime_type =~ /application|text/ && !Utils.binary?(file.data) + - if file.text? .view_file_content - - ft = handle_file_type(file.name, file.mime_type) :erb - <%= raw Albino.colorize(content, ft, :html, 'utf-8', "linenos=True") %> - - elsif file.mime_type =~ /image/ + <%= raw file.colorize %> + - elsif file.image? .view_file_content_image - %img{ :src => "data:image/jpeg;base64,#{Base64.encode64(file.data)}"} + %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} - else %p %center No preview for this file type diff --git a/app/views/projects/_tree_item.html.haml b/app/views/projects/_tree_item.html.haml index 9ba33c1e..4ebcfbef 100644 --- a/app/views/projects/_tree_item.html.haml +++ b/app/views/projects/_tree_item.html.haml @@ -12,4 +12,4 @@ = time_ago_in_words(content_commit.committed_date) ago %td - = link_to truncate(content_commit.message, :length => 40), project_commit_path(@project, content_commit) + = link_to truncate_commit_message(content_commit, 40), project_commit_path(@project, content_commit) diff --git a/app/views/projects/empty.html.erb b/app/views/projects/empty.html.erb index a8917471..4c60facd 100644 --- a/app/views/projects/empty.html.erb +++ b/app/views/projects/empty.html.erb @@ -1,3 +1,4 @@ +<% bash_lexer = Pygments::Lexer[:bash] %>

Git global setup:

@@ -6,7 +7,7 @@ git config --global user.name "#{current_user.name}" git config --global user.email "#{current_user.email}" eos %> - <%= raw Albino.colorize(setup_str, :bash) %> + <%= raw bash_lexer.highlight(setup_str) %>

Next steps:

@@ -21,7 +22,7 @@ git remote add origin #{@project.url_to_repo} git push -u origin master eos %> - <%= raw Albino.colorize(repo_setup_str, :bash) %> + <%= raw bash_lexer.highlight(repo_setup_str) %>

Existing Git Repo?

@@ -31,7 +32,7 @@ git remote add origin #{@project.url_to_repo} git push -u origin master eos %> - <%= raw Albino.colorize(exist_repo_setup_str, :bash) %> + <%= raw bash_lexer.highlight(exist_repo_setup_str) %>

Remove this project?

diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 0fd9c8ea..85019ecb 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,3 +1,23 @@ %div - %div#tree-holder - = render :partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @commit.tree} + %h2.left History + .right + = form_tag project_path(@project), :method => :get do + .span-2 + = radio_button_tag :view, "recent", (params[:view] || "recent") == "recent", :onclick => "this.form.submit()", :id => "recent_view" + = label_tag "recent_view","Recent" + .span-2 + = radio_button_tag :view, "day", params[:view] == "day", :onclick => "this.form.submit()", :id => "day_view" + = label_tag "day_view","Today" + .span-2 + = radio_button_tag :view, "week", params[:view] == "week", :onclick => "this.form.submit()", :id => "week_view" + = label_tag "week_view","Week" + .clear + %hr +.span-11 + %h3 Commits + =render "projects/recent_commits" + +.span-11.right + %h3 Talk + =render "projects/recent_messages" + diff --git a/app/views/projects/wall.html.haml b/app/views/projects/wall.html.haml index 479bb3cf..ed22478c 100644 --- a/app/views/projects/wall.html.haml +++ b/app/views/projects/wall.html.haml @@ -1 +1,29 @@ +%div.wall_page + - if can? current_user, :write_note, @project + = render "notes/form" + .right + = form_tag wall_project_path(@project), :method => :get do + .span-2 + = radio_button_tag :view, "recent", (params[:view] || "recent") == "recent", :onclick => "this.form.submit()", :id => "recent_view" + = label_tag "recent_view","Recent" + .span-2 + = radio_button_tag :view, "day", params[:view] == "day", :onclick => "this.form.submit()", :id => "day_view" + = label_tag "day_view","Today" + .span-2 + = radio_button_tag :view, "week", params[:view] == "week", :onclick => "this.form.submit()", :id => "week_view" + = label_tag "week_view","Week" + .span-2 + = radio_button_tag :view, "all", params[:view] == "all", :onclick => "this.form.submit()", :id => "all_view" + = label_tag "all_view","All" + .clear + %br + %hr = render "notes/notes" + +:javascript + $(function(){ + $("#note_note").live("click", function(){ + $(this).css("height", "100px"); + $('.attach_holder').show(); + }); + }); diff --git a/app/views/projects/wall.js.haml b/app/views/projects/wall.js.haml new file mode 100644 index 00000000..5b9a34c0 --- /dev/null +++ b/app/views/projects/wall.js.haml @@ -0,0 +1,2 @@ +:plain + $("#notes-list").html("#{escape_javascript(render(:partial => 'notes/notes_list'))}"); diff --git a/app/views/snippets/_form.html.haml b/app/views/snippets/_form.html.haml new file mode 100644 index 00000000..571e2b06 --- /dev/null +++ b/app/views/snippets/_form.html.haml @@ -0,0 +1,22 @@ +%div + = form_for [@project, @snippet] do |f| + -if @snippet.errors.any? + %ul + - @snippet.errors.full_messages.each do |msg| + %li= msg + + %table.round-borders + %tr + %td= f.label :title + %td= f.text_field :title, :placeholder => "Example Snippet" + %tr + %td= f.label :file_name + %td= f.text_field :file_name, :placeholder => "example.rb" + %tr + %td{:colspan => 2} + = f.label :content, "Code" + %br + = f.text_area :content, :style => "height:240px;width:932px;" + + .actions.prepend-top + = f.submit 'Save', :class => "lbutton vm" diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml new file mode 100644 index 00000000..483ff42c --- /dev/null +++ b/app/views/snippets/_snippet.html.haml @@ -0,0 +1,11 @@ +%tr{ :id => dom_id(snippet), :class => "snippet", :url => project_snippet_path(@project, snippet) } + %td + = image_tag gravatar_icon(snippet.author.email), :class => "left", :width => 40, :style => "padding:0 5px;" + = truncate snippet.author.name, :lenght => 20 + %td= html_escape snippet.title + %td= html_escape snippet.file_name + %td + - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user + = link_to 'Edit', edit_project_snippet_path(@project, snippet), :class => "lbutton positive" + - if can?(current_user, :admin_snippet, @project) || snippet.author == current_user + = link_to 'Destroy', [@project, snippet], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{snippet.id}" diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml new file mode 100644 index 00000000..f81c0b8b --- /dev/null +++ b/app/views/snippets/edit.html.haml @@ -0,0 +1 @@ += render "snippets/form" diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml new file mode 100644 index 00000000..6e5dbde5 --- /dev/null +++ b/app/views/snippets/index.html.haml @@ -0,0 +1,14 @@ +%div + - if can? current_user, :write_snippet, @project + .left= link_to 'New Snippet', new_project_snippet_path(@project), :class => "lbutton vm" + + %table.round-borders#snippets-table + %tr + %th Author + %th Title + %th File name + %th + = render @snippets +:javascript + $('.delete-snippet').live('ajax:success', function() { + $(this).closest('tr').fadeOut(); }); diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml new file mode 100644 index 00000000..f81c0b8b --- /dev/null +++ b/app/views/snippets/new.html.haml @@ -0,0 +1 @@ += render "snippets/form" diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml new file mode 100644 index 00000000..899950b7 --- /dev/null +++ b/app/views/snippets/show.html.haml @@ -0,0 +1,22 @@ +%h2 + = "Snippet ##{@snippet.id} - #{@snippet.title}" + +.view_file + .view_file_header + %strong + = @snippet.file_name + %br/ + .view_file_content + :erb + <%= raw @snippet.colorize %> + +- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user + = link_to 'Edit', edit_project_snippet_path(@project, @snippet), :class => "lbutton positive" +- if can?(current_user, :admin_snippet, @project) || @snippet.author == current_user + = link_to 'Destroy', [@project, @snippet], :confirm => 'Are you sure?', :method => :delete, :class => "lbutton delete-snippet negative", :id => "destroy_snippet_#{@snippet.id}" +.clear +%br +.snippet_notes= render "notes/notes" + +.clear + diff --git a/app/views/team_members/_show.html.haml b/app/views/team_members/_show.html.haml index 6d310768..b9a68e6c 100644 --- a/app/views/team_members/_show.html.haml +++ b/app/views/team_members/_show.html.haml @@ -1,8 +1,10 @@ - user = member.user %tr{:id => dom_id(member)} %td - = image_tag gravatar_icon(user.email), :class => "left", :width => 40, :style => "padding:0 5px;" - = truncate user.name, :lenght => 16 + = link_to image_tag(gravatar_icon(user.email), :class => "left", :width => 40, :style => "padding:0 5px;"), project_team_member_path(@project, member) + + = link_to truncate(user.name, :lenght => 16), project_team_member_path(@project, member) + %td= truncate user.email, :lenght => 16 - if can? current_user, :admin_project, @project = form_for(member, :as => :team_member, :url => project_team_member_path(@project, member)) do |f| diff --git a/app/views/team_members/show.html.haml b/app/views/team_members/show.html.haml new file mode 100644 index 00000000..d07c54f8 --- /dev/null +++ b/app/views/team_members/show.html.haml @@ -0,0 +1,28 @@ +- user = @team_member.user +.span-2 + = image_tag gravatar_icon(user.email), :class => "left", :width => 60, :style => "padding-right:5px;" +%p + %b Name: + = user.name +%p + %b Email: + = user.email + +%br + +- unless user.skype.empty? + .div + %b Skype: + = user.skype + +- unless user.linkedin.empty? + .div + %b LinkedIn: + = user.linkedin + +- unless user.twitter.empty? + .div + %b Twitter: + = user.twitter + + diff --git a/config/initializers/grit_ext.rb b/config/initializers/grit_ext.rb new file mode 100644 index 00000000..4bd71003 --- /dev/null +++ b/config/initializers/grit_ext.rb @@ -0,0 +1,8 @@ +require 'grit' +require 'pygments' +require "utils" + +Grit::Blob.class_eval do + include Utils::FileHelper + include Utils::Colorize +end diff --git a/config/routes.rb b/config/routes.rb index acf92536..8a40a8fe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,6 +13,7 @@ Gitlab::Application.routes.draw do get "errors/gitosis" get "profile/password", :to => "profile#password" put "profile/password", :to => "profile#password_update" + put "profile/edit", :to => "profile#social_update" get "profile", :to => "profile#show" #get "profile/:id", :to => "profile#show" @@ -38,6 +39,8 @@ Gitlab::Application.routes.draw do } end + + resources :snippets resources :commits resources :team_members resources :issues do diff --git a/db/migrate/20111016183422_create_snippets.rb b/db/migrate/20111016183422_create_snippets.rb new file mode 100644 index 00000000..9b0bf201 --- /dev/null +++ b/db/migrate/20111016183422_create_snippets.rb @@ -0,0 +1,12 @@ +class CreateSnippets < ActiveRecord::Migration + def change + create_table :snippets do |t| + t.string :title + t.text :content + t.integer :author_id, :null => false + t.integer :project_id, :null => false + + t.timestamps + end + end +end diff --git a/db/migrate/20111016193417_add_content_type_to_snippets.rb b/db/migrate/20111016193417_add_content_type_to_snippets.rb new file mode 100644 index 00000000..511a6793 --- /dev/null +++ b/db/migrate/20111016193417_add_content_type_to_snippets.rb @@ -0,0 +1,5 @@ +class AddContentTypeToSnippets < ActiveRecord::Migration + def change + add_column :snippets, :content_type, :string, :null => false, :default => "txt" + end +end diff --git a/db/migrate/20111016195506_add_file_name_to_snippets.rb b/db/migrate/20111016195506_add_file_name_to_snippets.rb new file mode 100644 index 00000000..d378d225 --- /dev/null +++ b/db/migrate/20111016195506_add_file_name_to_snippets.rb @@ -0,0 +1,6 @@ +class AddFileNameToSnippets < ActiveRecord::Migration + def change + add_column :snippets, :file_name, :string + remove_column :snippets, :content_type + end +end diff --git a/db/migrate/20111019212429_add_social_to_user.rb b/db/migrate/20111019212429_add_social_to_user.rb new file mode 100644 index 00000000..b0ffe536 --- /dev/null +++ b/db/migrate/20111019212429_add_social_to_user.rb @@ -0,0 +1,7 @@ +class AddSocialToUser < ActiveRecord::Migration + def change + add_column :users, :skype, :string + add_column :users, :linkedin, :string + add_column :users, :twitter, :string + end +end diff --git a/db/migrate/20111021101550_change_social_fields_in_users.rb b/db/migrate/20111021101550_change_social_fields_in_users.rb new file mode 100644 index 00000000..6e506c1c --- /dev/null +++ b/db/migrate/20111021101550_change_social_fields_in_users.rb @@ -0,0 +1,14 @@ +class ChangeSocialFieldsInUsers < ActiveRecord::Migration + def up + remove_column :users, :skype + remove_column :users, :linkedin + remove_column :users, :twitter + + add_column :users, :skype, :string, {:null => false, :default => ''} + add_column :users, :linkedin, :string, {:null => false, :default => ''} + add_column :users, :twitter, :string, {:null => false, :default => ''} + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index ed37dbbb..cc805990 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111015154310) do +ActiveRecord::Schema.define(:version => 20111021101550) do create_table "issues", :force => true do |t| t.string "title" @@ -56,6 +56,16 @@ ActiveRecord::Schema.define(:version => 20111015154310) do t.integer "owner_id" end + create_table "snippets", :force => true do |t| + t.string "title" + t.text "content" + t.integer "author_id", :null => false + t.integer "project_id", :null => false + t.datetime "created_at" + t.datetime "updated_at" + t.string "file_name" + end + create_table "users", :force => true do |t| t.string "email", :default => "", :null => false t.string "encrypted_password", :limit => 128, :default => "", :null => false @@ -72,6 +82,9 @@ ActiveRecord::Schema.define(:version => 20111015154310) do t.string "name" t.boolean "admin", :default => false, :null => false t.integer "projects_limit", :default => 10 + t.string "skype", :default => "", :null => false + t.string "linkedin", :default => "", :null => false + t.string "twitter", :default => "", :null => false end add_index "users", ["email"], :name => "index_users_on_email", :unique => true diff --git a/lib/color.rb b/lib/color.rb index d5500aca..23feecf4 100644 --- a/lib/color.rb +++ b/lib/color.rb @@ -11,6 +11,10 @@ module Color colorize(text, "32m") end + def yellow(text) + colorize(text, "93m") + end + def command(string) `#{string}` if $?.to_i > 0 diff --git a/lib/gitosis.rb b/lib/gitosis.rb index 4cc5e6e3..cab7da2d 100644 --- a/lib/gitosis.rb +++ b/lib/gitosis.rb @@ -27,13 +27,11 @@ class Gitosis def configure status = Timeout::timeout(20) do File.open(File.join(Dir.tmpdir,"gitlabhq-gitosis.lock"), "w+") do |f| - begin + begin f.flock(File::LOCK_EX) - pull yield(self) push - ensure f.flock(File::LOCK_UN) end diff --git a/lib/utils.rb b/lib/utils.rb index 6e7460ed..e57121a3 100644 --- a/lib/utils.rb +++ b/lib/utils.rb @@ -1,8 +1,51 @@ module Utils - def self.binary?(string) - string.each_byte do |x| - x.nonzero? or return true + module FileHelper + def binary?(string) + string.each_byte do |x| + x.nonzero? or return true + end + false + end + + def image? + mime_type =~ /image/ + end + + def text? + mime_type =~ /application|text/ && !binary?(data) + end + end + + module Colorize + def colorize + system_colorize(data, name) + end + + def system_colorize(data, file_name) + ft = handle_file_type(file_name) + Pygments.highlight(data, :lexer => ft, :options => { :encoding => 'utf-8', :linenos => 'True' }) + end + + def handle_file_type(file_name, mime_type = nil) + if file_name =~ /(\.rb|\.ru|\.rake|Rakefile|\.gemspec|\.rbx|Gemfile)$/ + :ruby + elsif file_name =~ /\.py$/ + :python + elsif file_name =~ /(\.pl|\.scala|\.c|\.cpp|\.java|\.haml|\.html|\.sass|\.scss|\.xml|\.php|\.erb)$/ + $1[1..-1].to_sym + elsif file_name =~ /\.js$/ + :javascript + elsif file_name =~ /\.sh$/ + :bash + elsif file_name =~ /\.coffee$/ + :coffeescript + elsif file_name =~ /\.yml$/ + :yaml + elsif file_name =~ /\.md$/ + :minid + else + :text + end end - false end end diff --git a/spec/factories.rb b/spec/factories.rb index ea055d1b..cc0cd4e5 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -35,6 +35,12 @@ Factory.add(:issue, Issue) do |obj| obj.content = Faker::Lorem.sentences end +Factory.add(:snippet, Snippet) do |obj| + obj.title = Faker::Lorem.sentence + obj.file_name = Faker::Lorem.sentence + obj.content = Faker::Lorem.sentences +end + Factory.add(:note, Note) do |obj| obj.note = Faker::Lorem.sentence end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 23609023..b2d594c9 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -38,5 +38,6 @@ end # created_at :datetime # updated_at :datetime # closed :boolean default(FALSE), not null +# position :integer default(0) # diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb new file mode 100644 index 00000000..9dab72ca --- /dev/null +++ b/spec/models/snippet_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Snippet do + describe "Associations" do + it { should belong_to(:project) } + it { should belong_to(:author) } + end + + describe "Validation" do + it { should validate_presence_of(:title) } + it { should validate_presence_of(:author_id) } + it { should validate_presence_of(:project_id) } + it { should validate_presence_of(:file_name) } + it { should validate_presence_of(:content) } + end +end +# == Schema Information +# +# Table name: snippets +# +# id :integer not null, primary key +# title :string(255) +# content :text +# author_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# file_name :string(255) +# + diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 77abe2ca..32fb90a3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -39,5 +39,8 @@ end # name :string(255) # admin :boolean default(FALSE), not null # projects_limit :integer +# skype :string +# linkedin :string +# twitter :string # diff --git a/spec/requests/profile_spec.rb b/spec/requests/profile_spec.rb index 07fdc4ab..5838f63b 100644 --- a/spec/requests/profile_spec.rb +++ b/spec/requests/profile_spec.rb @@ -14,6 +14,22 @@ describe "Profile" do it { page.should have_content(@user.email) } end + describe "Profile update" do + before do + visit profile_path + fill_in "user_skype", :with => "testskype" + fill_in "user_linkedin", :with => "testlinkedin" + fill_in "user_twitter", :with => "testtwitter" + click_button "Save" + @user.reload + end + + it { @user.skype.should == 'testskype' } + it { @user.linkedin.should == 'testlinkedin' } + it { @user.twitter.should == 'testtwitter' } + end + + describe "Password update" do before do visit profile_password_path diff --git a/spec/requests/projects_security_spec.rb b/spec/requests/projects_security_spec.rb index a725a49c..90f88d88 100644 --- a/spec/requests/projects_security_spec.rb +++ b/spec/requests/projects_security_spec.rb @@ -82,12 +82,18 @@ describe "Projects" do end describe "GET /project_code/blob" do - it { blob_project_path(@project).should be_allowed_for @u1 } - it { blob_project_path(@project).should be_allowed_for @u3 } - it { blob_project_path(@project).should be_denied_for :admin } - it { blob_project_path(@project).should be_denied_for @u2 } - it { blob_project_path(@project).should be_denied_for :user } - it { blob_project_path(@project).should be_denied_for :visitor } + before do + @commit = @project.commit + @path = @commit.tree.contents.select { |i| i.is_a?(Grit::Blob)}.first.name + @blob_path = blob_project_path(@project, :commit_id => @commit.id, :path => @path) + end + + it { @blob_path.should be_allowed_for @u1 } + it { @blob_path.should be_allowed_for @u3 } + it { @blob_path.should be_denied_for :admin } + it { @blob_path.should be_denied_for @u2 } + it { @blob_path.should be_denied_for :user } + it { @blob_path.should be_denied_for :visitor } end describe "GET /project_code/edit" do @@ -107,5 +113,14 @@ describe "Projects" do it { project_issues_path(@project).should be_denied_for :user } it { project_issues_path(@project).should be_denied_for :visitor } end + + describe "GET /project_code/snippets" do + it { project_snippets_path(@project).should be_allowed_for @u1 } + it { project_snippets_path(@project).should be_allowed_for @u3 } + it { project_snippets_path(@project).should be_denied_for :admin } + it { project_snippets_path(@project).should be_denied_for @u2 } + it { project_snippets_path(@project).should be_denied_for :user } + it { project_snippets_path(@project).should be_denied_for :visitor } + end end end diff --git a/spec/requests/projects_spec.rb b/spec/requests/projects_spec.rb index 2825a6a9..945c1ea2 100644 --- a/spec/requests/projects_spec.rb +++ b/spec/requests/projects_spec.rb @@ -72,7 +72,10 @@ describe "Projects" do current_path.should == project_path(@project) end - it_behaves_like :tree_view + it "should beahave like dashboard" do + page.should have_content("History") + end + end describe "GET /projects/team" do @@ -134,8 +137,6 @@ describe "Projects" do it "should show project" do page.should have_content("Awesome") end - - it_behaves_like :tree_view end #describe "DELETE /projects/:id", :js => true do diff --git a/spec/requests/snippets_spec.rb b/spec/requests/snippets_spec.rb new file mode 100644 index 00000000..00ae58da --- /dev/null +++ b/spec/requests/snippets_spec.rb @@ -0,0 +1,101 @@ +require 'spec_helper' + +describe "Snippets" do + let(:project) { Factory :project } + + before do + login_as :user + project.add_access(@user, :read, :write) + end + + describe "GET /snippets" do + before do + @snippet = Factory :snippet, + :author => @user, + :project => project + + visit project_snippets_path(project) + end + + subject { page } + + it { should have_content(@snippet.title) } + it { should have_content(@snippet.project.name) } + it { should have_content(@snippet.author.name) } + + describe "Destroy" do + before do + # admin access to remove snippet + @user.users_projects.destroy_all + project.add_access(@user, :read, :write, :admin) + visit project_snippets_path(project) + end + + it "should remove entry" do + expect { + click_link "destroy_snippet_#{@snippet.id}" + }.to change { Snippet.count }.by(-1) + end + end + end + + describe "New snippet" do + before do + visit project_snippets_path(project) + click_link "New Snippet" + end + + it "should open new snippet popup" do + page.current_path.should == new_project_snippet_path(project) + end + + describe "fill in" do + before do + fill_in "snippet_title", :with => "login function" + fill_in "snippet_file_name", :with => "test.rb" + fill_in "snippet_content", :with => "def login; end" + end + + it { expect { click_button "Save" }.to change {Snippet.count}.by(1) } + + it "should add new snippet to table" do + click_button "Save" + page.current_path.should == project_snippet_path(project, Snippet.last) + page.should have_content "login function" + page.should have_content "test.rb" + end + end + end + + describe "Edit snippet" do + before do + @snippet = Factory :snippet, + :author => @user, + :project => project + visit project_snippets_path(project) + click_link "Edit" + end + + it "should open edit page" do + page.current_path.should == edit_project_snippet_path(project, @snippet) + end + + describe "fill in" do + before do + fill_in "snippet_title", :with => "login function" + fill_in "snippet_file_name", :with => "test.rb" + fill_in "snippet_content", :with => "def login; end" + end + + it { expect { click_button "Save" }.to_not change {Snippet.count} } + + it "should update snippet fields" do + click_button "Save" + + page.current_path.should == project_snippet_path(project, @snippet) + page.should have_content "login function" + page.should have_content "test.rb" + end + end + end +end diff --git a/spec/requests/team_members_spec.rb b/spec/requests/team_members_spec.rb index db7513ae..dd92febf 100644 --- a/spec/requests/team_members_spec.rb +++ b/spec/requests/team_members_spec.rb @@ -7,6 +7,15 @@ describe "TeamMembers" do @project.add_access(@user, :read, :admin) end + describe "View profile" do + it "should be available" do + visit(team_project_path(@project)) + find(:xpath, "//table[@id='team-table']//a[1]").click + page.should have_content @user.skype + page.should_not have_content 'Twitter' + end + end + describe "New Team member", :js => true do before do @user_1 = Factory :user diff --git a/spec/requests/user_security_spec.rb b/spec/requests/user_security_spec.rb index 3c923870..a27eb1ca 100644 --- a/spec/requests/user_security_spec.rb +++ b/spec/requests/user_security_spec.rb @@ -7,10 +7,10 @@ describe "Users Security" do end describe "GET /login" do - it { new_user_session_path.should be_denied_for @u1 } - it { new_user_session_path.should be_denied_for :admin } - it { new_user_session_path.should be_denied_for :user } - it { new_user_session_path.should be_allowed_for :visitor } + #it { new_user_session_path.should be_denied_for @u1 } + #it { new_user_session_path.should be_denied_for :admin } + #it { new_user_session_path.should be_denied_for :user } + it { new_user_session_path.should_not be_404_for :visitor } end describe "GET /keys" do diff --git a/spec/support/login.rb b/spec/support/login.rb index 09f64f9e..462647ab 100644 --- a/spec/support/login.rb +++ b/spec/support/login.rb @@ -3,7 +3,8 @@ module LoginMacros @user = User.create(:email => "user#{User.count}@mail.com", :name => "John Smith", :password => "123456", - :password_confirmation => "123456") + :password_confirmation => "123456", + :skype => 'user_skype') if role == :admin @user.admin = true diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 953b5356..dcdfa6d5 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -21,17 +21,30 @@ RSpec::Matchers.define :be_denied_for do |user| end end +RSpec::Matchers.define :be_404_for do |user| + match do |url| + include UrlAccess + url_404?(user, url) + end +end + module UrlAccess def url_allowed?(user, url) emulate_user(user) visit url - result = (current_path == url) + (page.status_code != 404 && current_path != new_user_session_path) end def url_denied?(user, url) emulate_user(user) visit url - result = (current_path != url) + (page.status_code == 404 || current_path == new_user_session_path) + end + + def url_404?(user, url) + emulate_user(user) + visit url + page.status_code == 404 end def emulate_user(user) diff --git a/update.rb b/update.rb index d7eec289..b81a4088 100644 --- a/update.rb +++ b/update.rb @@ -2,6 +2,10 @@ root_path = File.expand_path(File.dirname(__FILE__)) require File.join(root_path, "lib", "color") include Color +def version + File.read("VERSION") +end + # # ruby ./update.rb development # or test or production (default) # @@ -12,15 +16,29 @@ env = if envs.include?(ARGV[0]) "production" end -puts green " == Update for ENV=#{env}" +puts yellow "== RAILS ENV | #{env}" +current_version = version +puts yellow "Your version is #{current_version}" +puts yellow "Check for new version: $ git pull origin 1x" +`git pull origin 1x` # pull from origin -# pull from github -`git pull` +# latest version +if version == current_version + puts yellow "You have a latest version" +else + puts green "Update to #{version}" `bundle install` -# migrate db + # migrate db +if env == "development" +`bundle exec rake db:migrate RAILS_ENV=development` +`bundle exec rake db:migrate RAILS_ENV=test` +else `bundle exec rake db:migrate RAILS_ENV=#{env}` +end + + puts green "== Done! Now you can start/restart server" +end -puts green " == Done! Now you can start/restart server"