Merge remote-tracking branch 'origin/szzh' into szzh

This commit is contained in:
lizanle 2015-03-20 11:08:47 +08:00
commit cdad43fdfc
192 changed files with 524 additions and 10808 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@
.DS_Store
public/api_doc/
/.metadata
vendor/cache

20
Gemfile
View File

@ -1,5 +1,4 @@
source 'http://rubygems.org'
#source 'http://ruby.taobao.com'
source 'http://ruby.taobao.org'
#source 'http://ruby.sdutlinux.org/'
unless RUBY_PLATFORM =~ /w32/
@ -11,11 +10,11 @@ end
gem 'grape', '~> 0.9.0'
gem 'grape-entity'
gem 'seems_rateable', path: 'lib/seems_rateable'
gem 'seems_rateable', '~> 1.0.13'
gem "rails", "3.2.13"
gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0"
gem "coderay", "~> 1.0.6"
gem 'coderay', '~> 1.1.0'
gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby]
gem "builder", "3.0.0"
gem 'acts-as-taggable-on', '2.4.1'
@ -28,11 +27,16 @@ gem 'rails_kindeditor'
group :development do
gem 'grape-swagger'
#gem 'grape-swagger-ui', git: 'https://github.com/guange2015/grape-swagger-ui.git'
#gem 'puma'
gem 'puma' if RbConfig::CONFIG['host_os'] =~ /linux/
gem 'pry-rails'
gem 'pry-byebug'
gem 'better_errors', path: 'lib/better_errors'
gem 'rack-mini-profiler', path: 'lib/rack-mini-profiler'
if RUBY_VERSION >= '2.0.0'
gem 'pry-byebug'
else
gem 'pry-debugger'
end
gem 'pry-stack_explorer'
gem 'better_errors', '~> 1.1.0'
gem 'rack-mini-profiler', '~> 0.9.3'
end
group :test do

View File

@ -2,6 +2,7 @@
module Mobile
module Apis
class Comments < Grape::API
include ApplicationHelper
resource :comments do
desc '课程通知评论'
params do
@ -82,8 +83,8 @@ module Mobile
memo: {:subject => params[:subject],:content => '该贴来自手机App意见反馈'},
}
cs = CommentService.new
memo = cs.create_feedback cs_params, current_user
raise "commit failed #{memo.errors.full_messages}" if memo.new_record?
memo,message = cs.create_feedback cs_params, current_user
raise message if memo.new_record?
present :status, 0
end

View File

@ -873,13 +873,15 @@ class CoursesController < ApplicationController
"show_course_news" => true,
"show_course_messages" => true,
"show_bids" => true,
"show_course_journals_for_messages" => true
"show_course_journals_for_messages" => true,
"show_homeworks" => true
}
@date_to ||= Date.today + 1
#
@date_from = (@date_to - @days) > @course.created_at.to_date ? (@date_to - @days) : @course.created_at.to_date
#@date_from = @date_to - @days-1.years
@author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
@author ||= @course.teacher
# 决定显示所用用户或单个用户活动
@activity = Redmine::Activity::Fetcher.new(User.current, :course => @course,
:with_subprojects => false,

View File

@ -20,7 +20,7 @@ class ForumsController < ApplicationController
#@memo.author_id = User.current.id
#@forum = @memo.forum
cs = CommentService.new
@memo = cs.create_feedback params,User.current
@memo,message = cs.create_feedback params,User.current
respond_to do |format|
if !@memo.new_record?
format.html { redirect_to forum_path(@memo.forum) }

View File

@ -0,0 +1,9 @@
class GitCallbackController < ApplicationController
def post_update
@repository = Repository.find_by_root_url(params[:root_url])
@repository.fetch_changesets
render :text => 'success'
end
end

View File

@ -58,7 +58,7 @@ class IssuesController < ApplicationController
def index
retrieve_query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
sort_init(@query.sort_criteria.empty? ? [['updated_on', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns)
@query.sort_criteria = sort_criteria.to_a
@ -381,7 +381,7 @@ class IssuesController < ApplicationController
def retrieve_previous_and_next_issue_ids
retrieve_query_from_session
if @query
sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
sort_init(@query.sort_criteria.empty? ? [['updated_on', 'desc']] : @query.sort_criteria)
sort_update(@query.sortable_columns, 'issues_index_sort')
limit = 500
issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])

View File

@ -109,7 +109,7 @@ class MembersController < ApplicationController
end
if params[:flag]
unless members.present? && members.all? {|m| m.valid? }
flash[:error] = members.collect {|m| m.errors.full_messages}.flatten.uniq.join(', ')
flash[:error] = members.empty? ? l(:label_user_role_null) :members.collect {|m| m.errors.full_messages}.flatten.uniq.join(', ')
else
flash[:notice] = l(:label_invite_success)
end

View File

@ -340,6 +340,7 @@ class ProjectsController < ApplicationController
@is_zhuce =false
flash[:notice] = l(:notice_email_sent, :value => email)
else
flash[:error] = l(:notice_registed_success, :value => email)
@is_zhuce = true
end
respond_to do |format|

View File

@ -92,6 +92,22 @@ class RepositoriesController < ApplicationController
render :action => 'show', :layout => 'base_projects'
end
HOOK_TEMPLATE = %Q{#!/bin/sh
exec sh -c '
function update()
{
CMD_PATH=`dirname $0`;
cd $CMD_PATH;
PY_PATH=$PWD/../../git_refresh_changes.py;
[[ -s "$PY_PATH" ]] && $(which python) $PY_PATH $PWD;
cd -;
}
git update-server-info
update
'
}
def create
if params[:repository_scm].to_s == 'Gitlab'
# add by nwb
@ -127,7 +143,6 @@ class RepositoriesController < ApplicationController
if attrs[:attrs_extra].keys.any?
@repository.merge_extra_info(attrs[:attrs_extra])
end
#by xianbo
@repository.project = @project
if request.post? && @repository.save
@ -145,12 +160,11 @@ class RepositoriesController < ApplicationController
"</Limit> \n ' >> "+
@root_path+"htdocs/"+ @repository_name+"/.htaccess"
system "cd "+@project_path+" ;git update-server-info"
# if(create_repo_file&&create_passwd&&create_group&&init_repository&&add_privilege&&init_server_info)
# else
# logger.info "An error occured when authenticating "+"create passwd"+@creat_passwd+"create_group"+
# crate_group+"create repository file "+create_repo_file+"init repository"+init_repostory+
# "aad privilege to rpository"+add_privilege+"init server infos"+init_server_info
# end
File.open(@project_path+"/hooks/post-update", "w+") do |f|
f.write(HOOK_TEMPLATE)
end
@repository.update_attributes(:login => User.current.login.to_s)
end
redirect_to settings_project_url(@project, :tab => 'repositories')
@ -160,6 +174,8 @@ class RepositoriesController < ApplicationController
render :action => 'new', :layout =>'base_projects'
end
end
end
end

View File

@ -445,15 +445,15 @@ class UsersController < ApplicationController
else
activity = Activity.where(where_condition).where('user_id = ?', @user.id).order('id desc')
end
activity = activity.reject { |e|
e.act.nil? ||
(!User.current.admin? && !e.act.nil?
(((e.act_type == "Issue") && !e.act.project.visible?(User.current)) ||
(e.act_type == "Bid" && !e.act.courses.first.nil? && e.act.courses.first.is_public == 0 && !User.current.member_of_course?(e.act.courses.first)) ||
(e.act_type == "Journal" && e.act.respond_to?("Project") && !e.act.project.visible?(User.current)) ||
(e.act_type == "News" && ((!e.act.project.nil? && !e.act.project.visible?(User.current)) || (!e.act.course.nil? && e.act.course.is_public == 0 && !User.current.member_of_course?(e.act.course)))) ||
(e.act_type == "Message" && !e.act.board.nil? && ((!e.act.board.project.nil? && !e.act.board.project.visible?(User.current)) || (!e.act.board.course.nil? && e.act.board.course.is_public == 0 && !User.current.member_of_course?(e.act.board.course))))))
}
# activity = activity.reject { |e|
# e.act.nil? ||
# (!User.current.admin? && !e.act.nil?
# (((e.act_type == "Issue") && !e.act.project.visible?(User.current)) ||
# (e.act_type == "Bid" && !e.act.courses.first.nil? && e.act.courses.first.is_public == 0 && !User.current.member_of_course?(e.act.courses.first)) ||
# (e.act_type == "Journal" && e.act.respond_to?("Project") && !e.act.project.visible?(User.current)) ||
# (e.act_type == "News" && ((!e.act.project.nil? && !e.act.project.visible?(User.current)) || (!e.act.course.nil? && e.act.course.is_public == 0 && !User.current.member_of_course?(e.act.course)))) ||
# (e.act_type == "Message" && !e.act.board.nil? && ((!e.act.board.project.nil? && !e.act.board.project.visible?(User.current)) || (!e.act.board.course.nil? && e.act.board.course.is_public == 0 && !User.current.member_of_course?(e.act.board.course))))))
# }
@activity_count = activity.count
@activity_pages = Paginator.new @activity_count, pre_count, params['page']
@activity = activity.slice(@activity_pages.offset,@activity_pages.per_page)

View File

@ -60,4 +60,8 @@ module ApiHelper
end
[count,is_teacher]
end
def get_user_language user
(user.language.nil? || user.language == "") ? 'zh':user.language
end
end

View File

@ -1994,4 +1994,10 @@ module ApplicationHelper
end
technical_title
end
def ie8?
request.env["HTTP_USER_AGENT"] =~ /MSIE 8.0/
end
end

View File

@ -43,7 +43,7 @@ module WatchersHelper
)
method = watched ? 'delete' : 'post'
link_to text, url, :remote => true, :method => method, :style => "color: #fff; display:block; padding: 0px 5px; margin-right: 10px; height: 22px; line-height: 22px; background: none repeat scroll 0% 0% #64BDD9; TES"
link_to text, url, :remote => true, :method => method, :style => "color: #fff; display:block; padding: 0px 5px; margin-right: 10px; height: 22px; line-height: 21px;padding-top:1px; background: none repeat scroll 0% 0% #64BDD9; TES"
end
############## added by linchun
@ -278,11 +278,11 @@ module WatchersHelper
)
method = applied ? 'delete' : 'post'
link_to text, url, :remote => true, :method => method ,:style => "color: #fff; display:block; padding: 0px 5px; margin-right: 10px; height: 22px; line-height: 22px; background: none repeat scroll 0% 0% #64BDD9; TES"
link_to text, url, :remote => true, :method => method ,:style => "color: #fff; display:block; padding: 0px 5px; margin-right: 10px; height: 21px; line-height: 22px;padding-top:1px; background: none repeat scroll 0% 0% #64BDD9; TES"
end
def exit_project_link(project)
link_to(l(:label_exit_project),exit_cur_project_path(project.id),
:remote => true, :confirm => l(:lable_sure_exit_project), :style => "color: #fff; display:block; padding: 0px 5px; margin-right: 10px; height: 22px; line-height: 22px; background: none repeat scroll 0% 0% #64BDD9; TES" )
:remote => true, :confirm => l(:lable_sure_exit_project), :style => "color: #fff; display:block; padding: 0px 5px; margin-right: 10px; height: 21px; line-height: 22px; background: none repeat scroll 0% 0% #64BDD9; TES;padding-top:1px;" )
end
end

View File

@ -61,14 +61,21 @@ class Bid < ActiveRecord::Base
end
}
scope :course_visible, lambda {|*args|
includes(:courses).where(Course.allowed_to_condition(args.shift || User.current, :view_homeworks, *args))
}
acts_as_watchable
acts_as_taggable
acts_as_event :title => Proc.new {|o| "#{l(:label_requirement)} ##{o.id}: #{o.name}" },
acts_as_event :title => Proc.new {|o| "#{l(:label_course_homework)} ##{o.id}: #{o.name}" },
:description => :description,
:author => :author,
:url => Proc.new {|o| {:controller => 'bids', :action => 'show', :id => o.id}}
acts_as_activity_provider :type => 'homeworks',
:author_key => :author_id
acts_as_activity_provider :find_options => {:include => [:projects, :author]},
:author_key => :author_id

View File

@ -778,7 +778,7 @@ class User < Principal
(block_given? ? yield(role, self) : true)
}
#添加课程相关的权限判断
elsif context && context.is_a?(Course)
elsif context && context.is_a?(Course)
return false unless context.allows_to?(action)
# Admin users are authorized for anything else
return true if admin?

View File

@ -32,40 +32,42 @@ class UserExtensions < ActiveRecord::Base
def get_brief_introduction
return self.brief_introduction
end
# added by meng
def show_identity
if self.identity == 0
if User.current.language == 'zh'
user_identity = '教师'
else
user_identity = 'Teacher'
end
elsif self.identity == 1
if User.current.language == 'zh'
user_identity = '学生'
else
user_identity = 'Student'
end
elsif self.identity == 2
if User.current.language == 'zh'
user_identity = '企业'
else
user_identity = 'Enterprise'
end
elsif self.identity == 3
if User.current.language == 'zh'
user_identity = '开发者'
else
user_identity = 'Developer'
if User.current.language == 'zh'||User.current.language == ''
case self.identity
when 0
user_identity = l(:label_account_identity_teacher)
when 1
user_identity = l(:label_account_identity_student)
when 2
user_identity = l(:label_account_identity_enterprise)
when 3
user_identity = l(:label_account_identity_developer)
else
user_identity = ''
end
else
user_identity = ''
case self.identity
when 0
user_identity = l(:label_account_identity_teacher)
when 1
user_identity = l(:label_account_identity_student)
when 2
user_identity = l(:label_account_identity_enterprise)
when 3
user_identity = l(:label_account_identity_developer)
else
user_identity = ''
end
end
return user_identity
end
# end
# end
def self.introduction(user, message)
unless user.user_extensions.nil?
info = user.user_extensions

View File

@ -1,4 +1,6 @@
class CommentService
include ApiHelper
include Redmine::I18n
#评论
def news_comments params,current_user
@news = News.find(params[:id])
@ -84,7 +86,8 @@ class CommentService
@memo.forum_id = "1"
@memo.author_id = current_user.id
@memo.save
@memo
message = "#{l(:label_commit_failed,:locale => get_user_language(current_user))}: #{@memo.errors.full_messages}" if @memo.new_record?
[@memo,message]
end
#课程留言列表

View File

@ -346,7 +346,7 @@ class CoursesService
membership = @user.coursememberships.all(:conditions => Course.visible_condition(current_user))
end
if membership.nil? || membership.count == 0
raise l(:label_no_courses,:locale => current_user.language.nil? ? 'zh':current_user.language)
raise l(:label_no_courses,:locale => get_user_language(current_user))
end
membership.sort! {|older, newer| newer.created_on <=> older.created_on }
result = []
@ -355,19 +355,19 @@ class CoursesService
latest_course_dynamics = []
latest_news = course.news.order("created_on desc").first
unless latest_news.nil?
latest_course_dynamics << {:type => 1,:time => latest_news.created_on,:message => l(:label_recently_updated_notification,:locale => current_user.language.nil? ? 'zh':current_user.language)}
latest_course_dynamics << {:type => 1,:time => latest_news.created_on,:message => l(:label_recently_updated_notification,:locale => get_user_language(current_user))}
end
latest_message = course.journals_for_messages.order("created_on desc").first
unless latest_message.nil?
latest_course_dynamics << {:type => 2,:time => latest_message.created_on,:message => l(:label_recently_updated_message,:locale => current_user.language.nil? ? 'zh':current_user.language)}
latest_course_dynamics << {:type => 2,:time => latest_message.created_on,:message => l(:label_recently_updated_message,:locale => get_user_language(current_user))}
end
latest_attachment = course.attachments.order("created_on desc").first
unless latest_attachment.nil?
latest_course_dynamics << {:type => 3,:time => latest_attachment.created_on,:message => l(:label_recently_updated_courseware,:locale => current_user.language.nil? ? 'zh':current_user.language)}
latest_course_dynamics << {:type => 3,:time => latest_attachment.created_on,:message => l(:label_recently_updated_courseware,:locale => get_user_language(current_user))}
end
latest_bid = course.homeworks.order('updated_on DESC').first
unless latest_bid.nil?
latest_course_dynamics << {:type => 4,:time => latest_bid.updated_on,:message => l(:label_recently_updated_homework,:locale => current_user.language.nil? ? 'zh':current_user.language)}
latest_course_dynamics << {:type => 4,:time => latest_bid.updated_on,:message => l(:label_recently_updated_homework,:locale => get_user_language(current_user))}
end
#每个作业中的最新留言
messages = []
@ -382,7 +382,7 @@ class CoursesService
end
latest_bid_message = messages.first
unless latest_bid_message.nil?
latest_course_dynamics << {:type => 4,:time => latest_bid_message.created_on,:message => l(:label_recently_updated_message,:locale => current_user.language.nil? ? 'zh':current_user.language)}
latest_course_dynamics << {:type => 4,:time => latest_bid_message.created_on,:message => l(:label_recently_updated_message,:locale => get_user_language(current_user))}
end
#每个作业中学生最后提交的作业
homeworks = []
@ -397,7 +397,7 @@ class CoursesService
end
latest_homework_attach = homeworks.first
unless latest_homework_attach.nil?
latest_course_dynamics << {:type => 4,:time => latest_homework_attach.updated_at,:message => l(:label_recently_updated_homework,:locale => current_user.language.nil? ? 'zh':current_user.language)}
latest_course_dynamics << {:type => 4,:time => latest_homework_attach.updated_at,:message => l(:label_recently_updated_homework,:locale => get_user_language(current_user))}
end
latest_course_dynamics.sort!{|order,newer| newer[:time] <=> order[:time]}
latest_course_dynamic = latest_course_dynamics.first

View File

@ -1,6 +1,6 @@
<span id="attachments_fields" xmlns="http://www.w3.org/1999/html">
<% if defined?(container) && container && container.saved_attachments %>
<% container.attachments.each_with_index do |attachment, i| %>
<% container.saved_attachments.each_with_index do |attachment, i| %>
<span id="attachments_p<%= i %>" class="attachment">
<%= text_field_tag("attachments[p#{i}][filename]", attachment.filename, :class => 'filename readonly', :readonly=>'readonly')%>
<%= text_field_tag("attachments[p#{i}][description]", attachment.description, :maxlength => 254, :placeholder => l(:label_optional_description), :class => 'description', :style=>"display: inline-block;") %>
@ -34,7 +34,6 @@
<%= hidden_field_tag "attachments[p#{i}][token]", "#{attachment.token}" %>
</span>
<% end %>
<% end %>
</span>
<script type='text/javascript'>
@ -49,13 +48,13 @@
<span class="add_attachment" style="font-weight:normal;">
<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
<!--%= link_to image_tag(),"javascript:void(0)", :onclick => "_file.click()"%-->
<%= button_tag "浏览", :type=>"button", :onclick=>"_file.click()",:onmouseover => 'this.focus()' %>
<%= button_tag l(:button_browse), :type=>"button", :onclick=>"_file.click()",:onmouseover => 'this.focus()', :style => ie8? ? 'display:none' : '' %>
<%= file_field_tag 'attachments[dummy][file]',
:id => '_file',
:class => 'file_selector',
:multiple => true,
:onchange => 'addInputFiles(this);',
:style => 'display:none',
:style => ie8? ? '' : 'display:none',
:data => {
:max_file_size => Setting.attachment_max_size.to_i.kilobytes,
:max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),

View File

@ -38,7 +38,7 @@
<span class="add_attachment">
<%#= button_tag "浏览", :type=>"button", :onclick=>"CompatibleSend();" %>
<!--%= link_to image_tag(),"javascript:void(0)", :onclick => "_file.click()"%-->
<%= button_tag "浏览", :type=>"button", :onclick=>"_file.click()" %>
<%= button_tag l(:button_browse), :type=>"button", :onclick=>"_file.click()" %>
<%= file_field_tag 'attachments[dummy][file]',
:id => '_file',
:class => 'file_selector',

View File

@ -1,7 +1,7 @@
$('#attachments_<%= j params[:attachment_id] %>').remove();
var count=$('#attachments_fields>span').length;
if(count<=0){
$("#upload_file_count").text("未上传文件");
$("#upload_file_count").text(<%= l(:label_no_file_uploaded)%>);
$(".remove_all").remove();
}else{
$("#upload_file_count").html("已上传"+"<span id=\"count\">"+count+"</span>"+"个文件");

View File

@ -22,13 +22,13 @@
</span>
</div>
<button name="button" class="f_l ml10" onclick="_file.click()" onmouseover="this.focus()" type="button" style="width:20%; height:26%;"><%= l(:label_browse)%></button>
<button name="button" class="f_l ml10" onclick="_file.click()" onmouseover="this.focus()" type="button" style="<%= ie8? ? 'display:none' : '' %>; width:20%; height:26%;"><%= l(:label_browse)%></button>
<%= file_field_tag 'attachments[dummy][file]',
:id => '_file',
:class => 'file_selector',
:multiple => true,
:onchange => 'addInputFiles(this);',
:style => 'display:none',
:style => ie8? ? '': 'display:none',
:data => {
:max_file_size => Setting.attachment_max_size.to_i.kilobytes,
:max_file_size_message => l(:error_attachment_too_big, :max_size => number_to_human_size(Setting.attachment_max_size.to_i.kilobytes)),

View File

@ -8,10 +8,10 @@
<table>
<tr>
<% if versions.any? %>
<td><p><%= l(:field_version) %></p></td>
<td><p style="padding-left: 50px;"><%= l(:field_version) %></p></td>
<td>
<%= select_tag "version_id", content_tag('option', '') +
options_from_collection_for_select(versions, "id", "name"), {style: 'width:100px'} %>
options_from_collection_for_select(versions, "id", "name"), {style: 'width:230px'} %>
</td>
<% if attachmenttypes.any? %>
<td><%= l(:attachment_type) %></label></td>
@ -31,14 +31,10 @@
<% end %>
</p>
<% end %>
</tr>
</table>
<p style="padding-left: 54px;"> <%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form', locals: {project: project} %></p>
</p>
<p><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form', locals: {project: project} %></p>
</div>
<%= submit_tag l(:button_add) %>
<% end %>

View File

@ -4,7 +4,8 @@
<div class="upload_box">
<%= error_messages_for 'attachment' %>
<div id="network_issue" style="color: red; display: none;"><%= l(:label_file_upload_error_messages)%></div>
<%= form_tag(course_files_path(course), :multipart => true,:remote => true,:method => :post,:name=>"upload_form") do %>
<%= form_tag(course_files_path(course), :multipart => true,:remote => !ie8?,:name=>"upload_form") do %>
<!-- <label style="margin-top:3px;"><#%= l(:label_file_upload)%></label> -->
<%= render :partial => 'attachement_list',:locals => {:course => course} %>
<div class="cl"></div>

View File

@ -12,7 +12,7 @@
<% end %>
<%= watcher_link(@issue, User.current) %>
<%= link_to l(:button_copy), project_copy_issue_path(@project, @issue), :class => 'icon icon-copy' if User.current.allowed_to?(:add_issues, @project) %>
<%= link_to l(:button_delete), issue_path(@issue.id), :data => {:confirm => issues_destroy_confirmation_message(@issue)}, :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_issues, @project) %>
</div>

View File

@ -1,181 +1,181 @@
<div class="project_r_h">
<h2 class="project_h2"><%= l(:label_issue_plural) %></h2>
</div>
<%# html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
<% html_title "#{@issue.tracker.name} #{@issue.source_from}'#'#{@issue.project_index}: #{@issue.subject}" %>
<%= render :partial => 'action_menu' %>
<h3>
<%= issue_heading(@issue) %>
</h3>
<style type="text/css"></style>
<div class="<%= @issue.css_classes %> details">
<% if @prev_issue_id || @next_issue_id %>
<div class="next-prev-links contextual">
<%= link_to_if @prev_issue_id,
"\xc2\xab #{l(:label_previous)}",
(@prev_issue_id ? issue_path(Issue.find_by_id(@prev_issue_id)) : nil),
:title => "##{@prev_issue_id}" %> |
<% if @issue_position && @issue_count %>
<span class="position"><%= l(:label_item_position, :position => @issue_position, :count => @issue_count) %></span> |
<% end %>
<%= link_to_if @next_issue_id,
"#{l(:label_next)} \xc2\xbb",
(@next_issue_id ? issue_path(Issue.find_by_id(@next_issue_id)) : nil),
:title => "##{@next_issue_id}" %>
</div>
<% end %>
<!-- <%#= avatar(@issue.author, :size => "50") %> -->
<div class="subject" style="word-wrap: break-word;">
<%= render_issue_subject_with_tree(@issue) %>
</div>
<!-- 顶和踩 在这里添加 -->
<span id="praise_tread" style="float: right">
<%= render :partial => "/praise_tread/praise_tread",:locals => {:obj => @issue,:show_flag => true,:user_id =>User.current.id,:horizontal => false}%>
</span>
<p class="author">
<%= authoring @issue.created_on, @issue.author %>.
<% if @issue.created_on != @issue.updated_on %>
<%= l(:label_updated_time, time_tag(@issue.updated_on)).html_safe %>.
<% end %>
</p>
<!-- added by william -for tag -->
<div id="tags">
<%= render :partial => 'tags/tag', :locals => {:obj => @issue,:object_flag => "3" }%>
</div>
<table class="attributes">
<%= issue_fields_rows do |rows|
rows.left l(:field_status), h(@issue.status.name), :class => 'status'
rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority'
unless @issue.disabled_core_fields.include?('assigned_to_id')
#modified by nie
#modified by huang
rows.left l(:field_assigned_to), (image_tag url_to_avatar(@issue.assigned_to(@user)), :class => 'avatar').to_s.html_safe + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to'
end
# end huang
unless @issue.disabled_core_fields.include?('category_id')
rows.left l(:field_category), h(@issue.category ? @issue.category.name : "-"), :class => 'category'
end
unless @issue.disabled_core_fields.include?('fixed_version_id')
rows.left l(:field_fixed_version), (@issue.fixed_version ? link_to_version(@issue.fixed_version) : "-"), :class => 'fixed-version'
end
unless @issue.disabled_core_fields.include?('start_date')
rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date'
end
unless @issue.disabled_core_fields.include?('due_date')
rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date'
end
unless @issue.disabled_core_fields.include?('done_ratio')
rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress'
end
unless @issue.disabled_core_fields.include?('estimated_hours')
unless @issue.estimated_hours.nil?
rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours'
end
end
if User.current.allowed_to?(:view_time_entries, @project)
rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), project_issue_time_entries_path(@project, @issue)) : "-"), :class => 'spent-time'
end
end %>
<%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table>
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<% if @issue.description? %>
<div class="description">
<div class="contextual">
<%= link_to l(:button_quote), quoted_issue_path(@issue.id), :remote => true, :method => 'post', :class => 'icon icon-comment' if authorize_for('issues', 'edit') %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
</div>
<% end %>
<%= link_to_attachments @issue, :thumbnails => true %>
<% end -%>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<% if false # !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<div id="issue_tree">
<div class="contextual">
<%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %>
</div>
<p><strong><%=l(:label_subtask_plural)%></strong></p>
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
</div>
<% end %>
<% if @relations.present? || User.current.allowed_to?(:manage_issue_relations, @project) %>
<hr />
<div id="relations">
<%= render :partial => 'relations' %>
</div>
<% end %>
</div>
<% if @changesets.present? %>
<div id="issue-changesets">
<h3><%=l(:label_associated_revisions)%></h3>
<%= render :partial => 'changesets', :locals => { :changesets => @changesets} %>
</div>
<% end %>
<% if @journals.present? %>
<div id="history">
<h3><%=l(:label_history)%></h3>
<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
</div>
<% end %>
<div style="clear: both;"></div>
<%= render :partial => 'action_menu' %>
<div style="clear: both;"></div>
<% if @issue.editable? %>
<div id="update">
<h3><%= l(:button_update) %></h3>
<%= render :partial => 'edit' %>
</div>
<% end %>
<% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
<%= f.link_to 'PDF' %>
<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>
<% if User.current.allowed_to?(:add_issue_watchers, @project) ||
(@issue.watchers.present? && User.current.allowed_to?(:view_issue_watchers, @project)) %>
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %>
</div>
<% end %>
<% end %>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
<% end %>
<%= context_menu issues_context_menu_path %>
<div class="project_r_h">
<h2 class="project_h2"><%= l(:label_issue_plural) %></h2>
</div>
<%# html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
<% html_title "#{@issue.tracker.name} #{@issue.source_from}'#'#{@issue.project_index}: #{@issue.subject}" %>
<%= render :partial => 'action_menu' %>
<h3>
<%= issue_heading(@issue) %>
</h3>
<style type="text/css"></style>
<div class="<%= @issue.css_classes %> details">
<% if @prev_issue_id || @next_issue_id %>
<div class="next-prev-links contextual">
<%= link_to_if @prev_issue_id,
"\xc2\xab #{l(:label_previous)}",
(@prev_issue_id ? issue_path(Issue.find_by_id(@prev_issue_id)) : nil),
:title => "##{@prev_issue_id}" %> |
<% if @issue_position && @issue_count %>
<span class="position"><%= l(:label_item_position, :position => @issue_position, :count => @issue_count) %></span> |
<% end %>
<%= link_to_if @next_issue_id,
"#{l(:label_next)} \xc2\xbb",
(@next_issue_id ? issue_path(Issue.find_by_id(@next_issue_id)) : nil),
:title => "##{@next_issue_id}" %>
</div>
<% end %>
<!-- <%#= avatar(@issue.author, :size => "50") %> -->
<div class="subject" style="word-wrap: break-word;">
<%= render_issue_subject_with_tree(@issue) %>
</div>
<!-- 顶和踩 在这里添加 -->
<span id="praise_tread" style="float: right">
<%= render :partial => "/praise_tread/praise_tread",:locals => {:obj => @issue,:show_flag => true,:user_id =>User.current.id,:horizontal => false}%>
</span>
<p class="author">
<%= authoring @issue.created_on, @issue.author %>.
<% if @issue.created_on != @issue.updated_on %>
<%= l(:label_updated_time, time_tag(@issue.updated_on)).html_safe %>.
<% end %>
</p>
<!-- added by william -for tag -->
<div id="tags">
<%= render :partial => 'tags/tag', :locals => {:obj => @issue,:object_flag => "3" }%>
</div>
<table class="attributes">
<%= issue_fields_rows do |rows|
rows.left l(:field_status), h(@issue.status.name), :class => 'status'
rows.left l(:field_priority), h(@issue.priority.name), :class => 'priority'
unless @issue.disabled_core_fields.include?('assigned_to_id')
#modified by nie
#modified by huang
rows.left l(:field_assigned_to), (image_tag url_to_avatar(@issue.assigned_to(@user)), :class => 'avatar').to_s.html_safe + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to'
end
# end huang
unless @issue.disabled_core_fields.include?('category_id')
rows.left l(:field_category), h(@issue.category ? @issue.category.name : "-"), :class => 'category'
end
unless @issue.disabled_core_fields.include?('fixed_version_id')
rows.left l(:field_fixed_version), (@issue.fixed_version ? link_to_version(@issue.fixed_version) : "-"), :class => 'fixed-version'
end
unless @issue.disabled_core_fields.include?('start_date')
rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date'
end
unless @issue.disabled_core_fields.include?('due_date')
rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date'
end
unless @issue.disabled_core_fields.include?('done_ratio')
rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%"), :class => 'progress'
end
unless @issue.disabled_core_fields.include?('estimated_hours')
unless @issue.estimated_hours.nil?
rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours'
end
end
if User.current.allowed_to?(:view_time_entries, @project)
rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), project_issue_time_entries_path(@project, @issue)) : "-"), :class => 'spent-time'
end
end %>
<%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</table>
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<% if @issue.description? %>
<div class="description">
<div class="contextual">
<%= link_to l(:button_quote), quoted_issue_path(@issue.id), :remote => true, :method => 'post', :class => 'icon icon-comment' if authorize_for('issues', 'edit') %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
</div>
<% end %>
<%= link_to_attachments @issue, :thumbnails => true %>
<% end -%>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<% if false # !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<div id="issue_tree">
<div class="contextual">
<%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %>
</div>
<p><strong><%=l(:label_subtask_plural)%></strong></p>
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
</div>
<% end %>
<% if @relations.present? || User.current.allowed_to?(:manage_issue_relations, @project) %>
<hr />
<div id="relations">
<%= render :partial => 'relations' %>
</div>
<% end %>
</div>
<% if @changesets.present? %>
<div id="issue-changesets">
<h3><%=l(:label_associated_revisions)%></h3>
<%= render :partial => 'changesets', :locals => { :changesets => @changesets} %>
</div>
<% end %>
<% if @journals.present? %>
<div id="history">
<h3><%=l(:label_history)%></h3>
<%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
</div>
<% end %>
<div style="clear: both;"></div>
<%= render :partial => 'action_menu' %>
<div style="clear: both;"></div>
<% if @issue.editable? %>
<div id="update">
<h3><%= l(:button_update) %></h3>
<%= render :partial => 'edit' %>
</div>
<% end %>
<% other_formats_links do |f| %>
<%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
<%= f.link_to 'PDF' %>
<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'issues/sidebar' %>
<% if User.current.allowed_to?(:add_issue_watchers, @project) ||
(@issue.watchers.present? && User.current.allowed_to?(:view_issue_watchers, @project)) %>
<div id="watchers">
<%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %>
</div>
<% end %>
<% end %>
<% content_for :header_tags do %>
<%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
<% end %>
<%= context_menu issues_context_menu_path %>

View File

@ -194,7 +194,7 @@
</strong>
<% if show_more_fans?(@bid) %>
<span style="display:inline-block; font-size: 12px; float:right; margin-bottom: -4px;">
<%= link_to l(:label_more), :controller => 'bids', :action => 'show_bid_user'%>
<%= link_to l(:button_more), :controller => 'bids', :action => 'show_bid_user'%>
</span>
<% end %>
</div>
@ -217,7 +217,7 @@
</strong>
<% if show_more_bid_project?(@bid) %>
<span style="display:inline-block; font-size: 12px; float:right; margin-bottom: -4px;">
<%= link_to l(:label_more), :controller => 'bids', :action => 'show_project'%>
<%= link_to l(:button_more), :controller => 'bids', :action => 'show_project'%>
</span>
<% end %>
</div>
@ -244,7 +244,7 @@
</strong>
<% if show_more_participate?(@bid) %>
<span style="font-size: 12px; display: inline; float: right;" >
<%= link_to l(:label_more), :controller => "bids", :action => "show_participator"%>
<%= link_to l(:button_more), :controller => "bids", :action => "show_participator"%>
</span>
<% end %>
</div>

View File

@ -215,7 +215,7 @@
<%= l(:label_x_followers, :count => @contest.watcher_users.count) %>
</strong>
<% if show_more_fans?(@contest) %>
<span style="display:inline-block; font-size: 12px; float:right; margin-bottom: -4px;"><%= link_to l(:label_more), show_contest_user_contest_path(@contest) %></span>
<span style="display:inline-block; font-size: 12px; float:right; margin-bottom: -4px;"><%= link_to l(:button_more), show_contest_user_contest_path(@contest) %></span>
<% end %>
</div>
<div class="left_wf">

View File

@ -93,7 +93,7 @@
<%= image_tag(url_to_avatar(@project), :style => 'width:61px; height:61px;') %>
</div>
<div class="pr_info_id fl mb5">
ID:<%= @project.id %>
<%= l(:label_project_id)%><%= @project.id %>
</div>
<!--关注、申请加入/退出项目-->
<div id="join_exit_project_div">
@ -170,7 +170,7 @@
<% end%>
<% unless @project.enabled_modules.where("name = 'files'").empty? %>
<div class="subNav">
<%= link_to l(:label_course_file), project_files_path(@project), :style => "color:#3CA5C6" %>
<%= link_to l(:project_module_files), project_files_path(@project), :style => "color:#3CA5C6" %>
<% unless attaments_num == 0 %>
<span class="subnav_num">(<%= attaments_num %>)</span>
<% end %>
@ -189,7 +189,7 @@
</div>
<% end %>
<div class="subNav subNav_jiantou"><%= l(:label_more) %></div>
<div class="subNav subNav_jiantou"><%= l(:label_project_more) %></div>
<ul class="navContent" style="padding-left: 0px">
<%= render 'projects/tools_expand' %>
</ul>

View File

@ -258,10 +258,10 @@
<% else %>
<tr>
<td style=" float: right" width="70px" >
<%= l(:label_identity)%>
<span style="float: right"><%= l(:label_identity)%></span>
</td>
<td class="font_lighter_sidebar" style="padding-left: 0px" width="170px">
<%= l(:label_account_student) %>
<%= l(:label_account_identity_student) %>
</td>
</tr>
@ -269,10 +269,10 @@
<% elsif @user.user_extensions.identity == 3 %>
<tr>
<td style=" float: right" width="70px" >
<%= l(:label_identity)%>
<span style="float: right"><%= l(:label_identity)%></span>
</td>
<td class="font_lighter_sidebar" style="padding-left: 0px" width="170px">
<%= l(:label_account_developer) %>
<%= l(:label_account_identity_developer) %>
</td>
</tr>
<% end %>
@ -295,7 +295,7 @@
</strong>
<% if show_more_watchers?(@user) %>
<div style="font-size: 11px; display: inline; float: right; margin-top: 5px; margin-right: 20px" >
<%= link_to l(:label_more), :controller => "users", :action => "user_watchlist"%>
<%= link_to l(:button_more), :controller => "users", :action => "user_watchlist"%>
</div>
<% end %>
</div>
@ -319,7 +319,7 @@
</strong>
<% if show_more_fans?(@user) %>
<div style="font-size: 11px; display: inline; float: right; margin-top: 5px; margin-right: 20px" >
<%= link_to l(:label_more), :controller => "users", :action => "user_fanslist"%>
<%= link_to l(:button_more), :controller => "users", :action => "user_fanslist"%>
</div>
<% end %>
</div>

View File

@ -3,7 +3,7 @@
<div class="well">
<% next if member.new_record? %>
<% unless member.created_on.nil? %>
<%= content_tag "p", (User.current.language == ""|| User.current.language == "zh")?("#{format_date(member.created_on)}"+" "+"#{l(:label_member_since)}"):("#{l(:label_member_since)}+" "+#{format_date(member.created_on)}"), :class => "float_right member_since" %>
<%= content_tag "p", (User.current.language == ""|| User.current.language == "zh")?("#{format_date(member.created_on)}"+" "+"#{l(:label_member_since)}"):("#{l(:label_member_since)}"+" "+"#{format_date(member.created_on)}"), :class => "float_right member_since" %>
<% end %>
<%= member.user.nil? ? '' : (image_tag(url_to_avatar(member.user), :class => 'avatar')) %>
<%= content_tag "div", link_to(member.user.name, user_path(member.user)), :class => "nomargin avatar_name" %>

View File

@ -42,7 +42,7 @@
</li>
<li>
<% unless @project.enabled_modules.where("name = 'dts'").empty? %>
<%= link_to l(:label_module_share) ,share_show_path(@project) %>
<%= link_to l(:project_module_dts) ,share_show_path(@project) %>
<% end %>
</li>

View File

@ -17,7 +17,7 @@
<tr>
<td colspan="2" width="580px" ><p class="font_description">
<% unless user.memberships.empty? %>
<%= l(:label_contribute_to, :project_count => "#{user.memberships.count}") %>
<%= l(:label_contribute_to, :count => user.memberships.count) %>
<% for member in user.memberships %>
<%= link_to_project(member.project) %><%= (user.memberships.last == member) ? '' : '' %>
<% end %>

View File

@ -1,32 +1,30 @@
<div id="tags_show">
<div id="tags_show" style="float: left;">
<%= render :partial => "tags/tag_name",:locals => {:obj => obj,:non_list_all => false ,:object_flag => object_flag} %>
</div>
<div style="float: left;">
<% if User.current.logged? %>
<span> <%= toggle_link (l(:label_add_tag)), 'put-tag-form', {:focus => 'tags_name'} %> </span>
<% end %>
<div id="put-tag-form" style="display: none;text-align: center">
<%= form_for "tag_for_save",:remote=>true,:url=>tag_path,
:update => "tags_show",
:complete => '$("#put-tag-form").slideUp();' do |f| %>
<table>
:update => "tags_show",
:complete => '$("#put-tag-form").slideUp();' do |f| %>
<table>
<tr>
<td>
<%= f.text_field :name ,:id => "tags_name",:size=>"20",
:require=>true,
:maxlength => Setting.tags_max_length,
:minlength=>Setting.tags_min_length %>
:require=>true,
:maxlength => Setting.tags_max_length,
:minlength=>Setting.tags_min_length %>
</td>
<%= f.text_field :object_id,:value=> obj.id,:style=>"display:none"%>
<%= f.text_field :object_flag,:value=> object_flag,:style=>"display:none"%>
<td style="margin-left: 5px">
<td style="margin-left: 5px" vertical-valign="middle" >
<a href="#" onclick='$("#tags_name").parent().submit();' type="button" class="submit f_l"></a>
</td>
<tr>
</table>
<% end %>
</div>
</div>

View File

@ -31,7 +31,12 @@
<!-- 用来显示三大对象的主页中的tag 故是全部显示 -->
<% if @tags.size > 0 %>
<% @tags.each do |tag| %>
<div id="tag">
<!--项目暂时单独出来,后面重构-->
<% if object_flag == '2' %>
<span class="re_tag f_l">
<%= link_to tag, :controller => "tags", :action => "index", :q => tag, :object_flag => object_flag, :obj_id => obj.id %></span>
<% else %>
<div id="tag">
<span class="tag_show">
<%= link_to tag, :controller => "tags", :action => "index", :q => tag, :object_flag => object_flag, :obj_id => obj.id %>
<!-- 对用户主页 是本人 ,对项目,需求,问题是管理员 -->
@ -43,13 +48,6 @@
:taggable_id => obj.id, :taggable_type => object_flag %>
</span>
<% end %>
<% when '2' %>
<% if (ProjectInfo.find_by_project_id(obj.id)).try(:user_id) == User.current.id %>
<span class='del'>
<%= link_to 'x', :controller => "tags", :action => "remove_tag", :remote => true, :tag_name => tag,
:taggable_id => obj.id, :taggable_type => object_flag %>
</span>
<% end %>
<% when '3' %>
<% if (ProjectInfo.find_by_project_id(obj.project_id)).try(:user_id) == User.current.id %>
<span class='del'>
@ -101,6 +99,7 @@
<% end %>
</span>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>

View File

@ -10,7 +10,7 @@
</span>
<% end %>
<%= link_to( project.name, project_path(project.id), :class => "d-g-blue d-p-project-name",:title => "#{project.name}" )%>
(<%= link_to l(:label_project_member_amount, :count=>projectCount(project)), project_member_path(project) ,:course =>'0' %>)
(<%= link_to l(:label_project_member_amount, :count => projectCount(project)), project_member_path(project) ,:course =>'0' %>)
</div>
<div style=" float: left;margin:5px; margin-left: 10px; width: 380px;min-height: 42px;">
<span class='font_lighter' title ='<%= project.short_description%>'> <%=project.description.truncate(90, omission: '...')%> </span>

View File

@ -115,7 +115,7 @@
<div class="forum-topic" style="height: 25px; width: 98%; margin-left: 2px;">
<h3 style="color: rgb(21, 188, 207);"><strong> <%= l(:lable_bar_active)%> </strong> <%= link_to l(:label_my_question) , newbie_send_path, {:class => 'orangeButton idea_btn', :style => "color: #EEEEEE" }%>
<%= link_to l(:label_my_feedback) , suggestion_send_path, {:class => 'orangeButton idea_btn', :style => "color: #EEEEEE" }%> </h3>
<span style="margin-top: -30px;float: right; display: block;"> <%= link_to l(:label_more), forums_path %> </span>
<span style="margin-top: -30px;float: right; display: block;"> <%= link_to l(:button_more), forums_path %> </span>
</div>
<div class="welcome-box-list-new memo_activity">
<% topics = find_new_forum_topics(12) %>

View File

@ -163,7 +163,6 @@ en:
label_requirement: Calls
label_forum: Forum
label_contest: Contest
label_attachment_plural: Files
@ -203,12 +202,12 @@ en:
button_cancel: Cancel
label_submit: Submit
button_project_tags_add: Add
label_more: "More>>"
button_download: Download
button_more: "More»"
button_delete: Delete
button_unfollow: Unfollow
button_follow: Follow
button_browse: Browse
#

View File

@ -173,7 +173,7 @@ zh:
label_requirement: 需求
label_forum: 公共贴吧
label_contest: 竞赛
label_attachment_plural: 文件
field_description: 描述
@ -207,14 +207,16 @@ zh:
button_cancel: 取消
label_submit: 提交
button_project_tags_add: 增加
label_more: "更多>>"
button_download: 下载
button_more: 更多
button_more: "更多»"
button_delete: 删除
button_unfollow: 取消关注
button_follow: 关注
button_watch: 跟踪
button_unwatch: 取消跟踪
button_browse: 浏览
#
# Trustie上传头像模块
#

View File

@ -107,7 +107,6 @@ en:
field_last_login_on: Last connection
field_effective_date: Date
field_version: Version
field_type: Type
field_host: Host
field_port: Port
@ -479,7 +478,6 @@ en:
label_change_status: Change status
label_history: History
label_attachment: Files
label_attachment_new: New file
label_attachment_delete: Delete file
label_file_added: File added
@ -611,12 +609,10 @@ en:
label_latest_revision_plural: Latest revisions
label_view_revisions: View revisions
label_view_all_revisions: View all revisions
label_max_size: Maximum size
label_sort_highest: Move to top
label_sort_higher: Move up
label_sort_lower: Move down
label_sort_lowest: Move to bottom
label_roadmap: Roadmap
label_roadmap_due_in: "Due in %{value}"
label_roadmap_overdue: "%{value} late"
label_roadmap_no_issues: No issues for this version
@ -721,7 +717,6 @@ en:
label_plugins: Plugins
label_ldap_authentication: LDAP authentication
label_downloads_abbr: D/L
label_optional_description: Optional description
label_add_another_file: Add another file
label_preferences: Preferences
label_chronological_order: In chronological order
@ -1028,7 +1023,7 @@ en:
label_leave_message: Message content
label_message: message board
field_add: Add before %{time}
button_more: More
label_bidding_project: projects
button_bidding: I will participate in it
@ -1588,3 +1583,5 @@ en:
label_recently_updated_message: Recently updated the message
label_recently_updated_courseware: Recently updated the courseware
label_no_courses: You do not participate in any course, please search the curriculum, course, or create a course!
label_commit_failed: commit failed
#api end

View File

@ -19,7 +19,9 @@ en:
#
lable_hot_projects: Hot Projects
label_private: private
label_project_member_amount: "%{count} members"
label_project_member_amount:
one: "%{count} member"
other: "%{count} members"
label_project_score_tips: "Considering all activities of the project, project's score reflects the activity level of project"
label_project_score: Score
@ -29,7 +31,7 @@ en:
#
# 左边栏
#
label_id: "ID:"
label_project_id: "Projcet ID:"
label_apply_project: Apply to Join
label_exit_project: Exit
@ -37,7 +39,7 @@ en:
label_unapply_project: Cancel the application
label_member: Members
project_module_attachments: Files
project_module_attachments: Resources
label_invite: Invitation
label_invite_new_user: "Send e-mail to invite new user"
@ -48,19 +50,21 @@ en:
project_module_boards: Forums
project_module_boards_post: New Post
# 与课程公用资源库
project_module_files: Resources
label_upload_files: New File
project_module_repository: Repository
project_module_create_repository: New Repository
label_project_more: More
project_module_news: News
project_module_wiki: Wiki
project_module_code_review: Code Review
project_module_calendar: Calendar
project_module_gantt: Gantt
project_module_documents: Documents
label_project_tool_response: Response
label_module_share: DTS Test Tool
label_roadmap: Roadmap
label_project_tool_response: Feedback
project_module_dts: DTS Test Tool
label_project_overview: "Profile:"
label_expend_information: More Information
@ -87,7 +91,9 @@ en:
# 关注者列表
#
label_followers: Followers
label_contribute_to: "Participates %{project_count} projects—"
label_contribute_to:
one: "Participates %{count} project—"
other: "Participates %{count} projects—"
#
@ -95,10 +101,7 @@ en:
#
# 资源库
#
lable_file_sharingarea: Files
label_upload_files: Upload
lable_file_sharingarea: Resources
# 资源库(附件)公用
label_relation_files: Select an existing resource
@ -108,18 +111,26 @@ en:
attachment_all: "All"
attachment_browse: "Attachment Content Browse"
attachment_sufix_browse: "Attachment Type Browse"
attachment_type: "Attachment Type"
label_unknow_type: Unknow type
label_unknow_type: Unknow type
field_filename: File
field_filesize: Size
field_filecontenttype: Content Typ
field_filecontenttype: Content
field_filetype: File Typ
field_downloads: Downloads
field_file_dense: Dense
# 资源库(附件)公用 > 上传文件
label_attachment_new: New file
field_version: Version
attachment_type: "Attachment Type"
label_attachment_plural: Files
label_no_file_uploaded: No file uploaded
label_max_size: Maximum size
label_optional_description: Description
label_file_count: "%{count} files were uploaded successfully"
#
# 项目托管平台
@ -164,7 +175,6 @@ en:
project_module_issue_tracking: Issue tracking
project_module_time_tracking: Time tracking
project_module_course: 课程
project_module_files: Files
project_module_boards: Forums
#

View File

@ -22,7 +22,9 @@ zh:
#
lable_hot_projects: 热门项目
label_private: 私有
label_project_member_amount: "%{count}人"
label_project_member_amount:
one: "%{count}人"
other: "%{count}人"
label_project_score_tips: 项目得分,综合考虑了项目的各项活动,反映了该项目的活跃程度
label_project_score: 项目评分
@ -32,7 +34,7 @@ zh:
#
# 左边栏
#
label_id: "ID:"
label_project_id: "项目ID:"
label_apply_project: 申请加入
label_exit_project: 退出项目
@ -50,18 +52,20 @@ zh:
project_module_boards: 讨论区
project_module_boards_post: 发帖
# 与课程公用资源库
project_module_files: 资源库
project_module_repository: 版本库
project_module_create_repository: 创建版本库
label_project_more: 更多
project_module_news: 新闻
project_module_wiki: Wiki
project_module_code_review: 代码审查
project_module_calendar: 日历
project_module_gantt: 甘特图
project_module_documents: 文档
label_roadmap: 里程碑 #版本路线图
project_module_dts: DTS测试工具
label_project_tool_response: 用户反馈
label_module_share: DTS测试工具
label_project_overview: "项目简介:"
label_expend_information: 展开更多信息
@ -88,7 +92,9 @@ zh:
# 关注者列表
#
label_followers: 关注
label_contribute_to: 参与了 %{project_count} 个项目:
label_contribute_to:
one: "参与了 %{count}个项目:"
other: "参与了 %{count}个项目:"
#
@ -101,7 +107,7 @@ zh:
label_upload_files: 上传文件
# 资源库(附件)公用
# 资源库(附件)公用 > 关联资源
label_relation_files: 关联已有资源
label_search_by_keyword: "按关键字搜索:"
label_files_filter: "资源过滤:"
@ -116,8 +122,19 @@ zh:
attachment_sufix_browse: "文件类型"
attachment_browse: "内容类型"
attachment_all: "全部"
attachment_type: "分类"
label_unknow_type: 未知类型
# 资源库(附件)公用 > 上传文件
label_attachment_new: 新建文件
field_version: 版本
attachment_type: "分类"
label_attachment_plural: 文件
label_no_file_uploaded: 未上传文件
label_max_size: 最大文件大小
label_optional_description: 可选的描述
#
# 项目托管平台
@ -166,7 +183,6 @@ zh:
project_moule_boards_show: 项目论坛
project_module_time_tracking: 时间跟踪
project_module_course: 课程
project_module_files: 资源库
#
@ -179,6 +195,7 @@ zh:
label_invite_email_tips: 输入好友邮箱地址Trustie会自动为该邮箱注册用户
notice_registed_success: 您输入的邮箱为空或者该邮箱已被注册!
label_email_format_error: 您所填写的电子邮件格式不正确
label_user_role_null: 用户和角色不能留空!
label_send_email: 免费发送
label_input_email: 请输入邮箱地址

View File

@ -77,8 +77,6 @@ en:
label_technical_title: Title
label_bidding_user_studentcode: Student ID
label_account_developer: Developer
label_account_student: Student
#
@ -112,7 +110,7 @@ en:
label_layouts_feedback: "a message "
label_of_feedback: from
label_goto: Go to>>
label_goto: "Go to»"
label_activity_project: "Project"
label_active_call: call
@ -180,12 +178,12 @@ en:
#
label_responses: Messages
label_user_response: Feedback
label_leave_a_message: Leave him/her a message
label_leave_a_message: "Leave him/her a message"
button_leave_meassge: Submit
button_clear_meassge: Reset
label_user_login_new: Login
label_user_login_tips: You haven't logged in, please login first to leave a message!
label_user_login_tips: "You haven't logged in, please login first to leave a message!"
label_bid_respond_delete: Delete
label_bid_respond_quote: Respond

View File

@ -36,6 +36,7 @@ zh:
label_user_edit: "修改资料"
label_user_score: 个人综合得分
# 用户身份在/my的修改资料下
label_user_score_of_collaboration: 协同得分
label_user_score_of_influence: 影响力得分
label_user_score_of_skill: 技术得分
@ -87,10 +88,9 @@ zh:
label_technicl_title_associate_professor: 副教授
label_technicl_title_lecturer: 讲师
label_technicl_title_teaching_assistant: 助教
# 用户身份(学生、开发者)标签在/my的修改资料下
label_bidding_user_studentcode: 学号
label_account_developer: 开发者
label_account_student: 学生
label_no_current_fans: 该用户暂无粉丝
label_no_current_watchers: 该用户暂未关注其他用户
@ -115,7 +115,7 @@ zh:
label_of_feedback:
label_layouts_feedback: 留言
label_goto: 前往>>
label_goto: "前往»"
label_activity_project: "项目:"
label_active_call: 需求

View File

@ -129,13 +129,12 @@ zh:
field_priority: 优先级
field_fixed_version: 目标版本
field_user: 用户
field_principal: 用户/用户组
field_principal: 用户
field_role: 角色
field_homepage: 主页
field_time: 课时
field_class_period: 学时
field_code: 学分
field_is_public: 公开
field_open_student: 学生列表公开
field_parent: 上级项目
field_is_in_roadmap: 在路线图中显示
@ -144,7 +143,6 @@ zh:
field_last_login_on: 最后登录
field_language: 语言
field_effective_date: 日期
field_version: 版本
field_type: 类型
field_host: 主机
field_port: 端口
@ -465,8 +463,6 @@ zh:
label_new_contest: 竞赛
label_requirement_focus: 关注需求
label_developer: 用户
label_account_developer: 开发者
label_account_student: 学生
label_enterprise_into: 进入企业
label_college_into: 进入高校
label_investor: 投资人:
@ -561,7 +557,7 @@ zh:
label_change_status: 变更状态
label_history: 历史记录
label_attachment: 文件
label_attachment_new: 新建文件
label_file_upload: 上传资料
label_course_file_upload: 上传了课件
label_attachment_delete: 删除文件
@ -691,13 +687,11 @@ zh:
label_latest_revision_plural: 最近的修订版本
label_view_revisions: 查看修订
label_view_all_revisions: 查看所有修订
label_no_file_uploaded: 未上传文件
label_max_size: 最大文件大小
label_sort_highest: 置顶
label_sort_higher: 上移
label_sort_lower: 下移
label_sort_lowest: 置底
label_roadmap: 里程碑 #版本路线图
label_roadmap_due_in: "截止日期到 %{value}"
label_roadmap_overdue: "%{value} 延期"
label_roadmap_no_issues: 该版本没有问题
@ -846,7 +840,7 @@ zh:
label_plugins: 插件
label_ldap_authentication: LDAP 认证
label_downloads_abbr: D/L
label_optional_description: 可选的描述
label_add_another_file: 添加其它文件
label_preferences: 首选项
label_chronological_order: 按时间顺序
@ -1093,7 +1087,7 @@ zh:
description_all_columns: 所有列
button_export: 导出
label_export_options: "%{export_format} 导出选项"
error_attachment_too_big: 该文件无法上传。超过文件大小限制 (%{max_size})
error_pic_type: "仅支持如下图片格式:"
notice_failed_to_save_time_entries: "无法保存下列所选取的 %{total} 个项目中的 %{count} 工时: %{ids}。"
label_x_issues:
@ -2032,6 +2026,9 @@ zh:
label_recently_updated_message: 最近更新了留言
label_recently_updated_courseware: 最近更新了课件
label_no_courses: 您没有参与任何课程,请搜索课程、加入课程,或者创建课程吧!
label_commit_failed: 提交失败
#api end
label_end_time: 截止时间
label_send_email: 确定发送
label_input_email: 请输入邮箱地址

View File

@ -863,6 +863,11 @@ RedmineApp::Application.routes.draw do
match 'system_log/clear'
##ended by lizanle
resources :git_callback do
collection do
post 'post_update'
end
end
Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
file = File.join(plugin_dir, "config/routes.rb")

Binary file not shown.

View File

@ -1,4 +0,0 @@
language: ruby
rvm:
- 2.1.0
- 2.0.0

View File

@ -1 +0,0 @@
--markup markdown --no-private

View File

@ -1,3 +0,0 @@
# Changelog
See https://github.com/charliesome/better_errors/releases

View File

@ -1,10 +0,0 @@
source 'https://rubygems.org'
gemspec
gem "rake"
gem "rspec", "2.14.1"
gem "binding_of_caller", platforms: :ruby
gem "pry", "0.9.12"
gem "yard"
gem "kramdown"

View File

@ -1,22 +0,0 @@
Copyright (c) 2014 Charlie Somerville
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,103 +0,0 @@
# Better Errors [![Gem Version](http://img.shields.io/gem/v/better_errors.svg)](https://rubygems.org/gems/better_errors) [![Build Status](https://travis-ci.org/charliesome/better_errors.svg)](https://travis-ci.org/charliesome/better_errors) [![Code Climate](http://img.shields.io/codeclimate/github/charliesome/better_errors.svg)](https://codeclimate.com/github/charliesome/better_errors)
Better Errors replaces the standard Rails error page with a much better and more useful error page. It is also usable outside of Rails in any Rack app as Rack middleware.
![image](http://i.imgur.com/6zBGAAb.png)
## Features
* Full stack trace
* Source code inspection for all stack frames (with highlighting)
* Local and instance variable inspection
* Live REPL on every stack frame
## Installation
Add this to your Gemfile:
```ruby
group :development do
gem "better_errors"
end
```
If you would like to use Better Errors' **advanced features** (REPL, local/instance variable inspection, pretty stack frame names), you need to add the [`binding_of_caller`](https://github.com/banister/binding_of_caller) gem by [@banisterfiend](http://twitter.com/banisterfiend) to your Gemfile:
```ruby
gem "binding_of_caller"
```
This is an optional dependency however, and Better Errors will work without it.
_Note: If you discover that Better Errors isn't working - particularly after upgrading from version 0.5.0 or less - be sure to set `config.consider_all_requests_local = true` in `config/environments/development.rb`._
## Security
**NOTE:** It is *critical* you put better\_errors in the **development** section. **Do NOT run better_errors in production, or on Internet facing hosts.**
You will notice that the only machine that gets the Better Errors page is localhost, which means you get the default error page if you are developing on a remote host (or a virtually remote host, such as a Vagrant box). Obviously, the REPL is not something you want to expose to the public, but there may also be other pieces of sensitive information available in the backtrace.
To poke selective holes in this security mechanism, you can add a line like this to your startup (for example, on Rails it would be `config/environments/development.rb`)
```ruby
BetterErrors::Middleware.allow_ip! ENV['TRUSTED_IP'] if ENV['TRUSTED_IP']
```
Then run Rails like this:
```shell
TRUSTED_IP=66.68.96.220 rails s
```
Note that the `allow_ip!` is actually backed by a `Set`, so you can add more than one IP address or subnet.
**Tip:** You can find your apparent IP by hitting the old error page's "Show env dump" and looking at "REMOTE_ADDR".
**VirtualBox:** If you are using VirtualBox and are accessing the guest from your host's browser, you will need to use `allow_ip!` to see the error page.
## Usage
If you're using Rails, there's nothing else you need to do.
If you're not using Rails, you need to insert `BetterErrors::Middleware` into your middleware stack, and optionally set `BetterErrors.application_root` if you'd like Better Errors to abbreviate filenames within your application.
Here's an example using Sinatra:
```ruby
require "sinatra"
require "better_errors"
configure :development do
use BetterErrors::Middleware
BetterErrors.application_root = __dir__
end
get "/" do
raise "oops"
end
```
### Unicorn, Puma, and other multi-worker servers
Better Errors works by leaving a lot of context in server process memory. If
you're using a web server that runs multiple "workers" it's likely that a second
request (as happens when you click on a stack frame) will hit a different
worker. That worker won't have the necessary context in memory, and you'll see
a `Session Expired` message.
If this is the case for you, consider turning the number of workers to one (1)
in `development`. Another option would be to use Webrick, Mongrel, Thin,
or another single-process server as your `rails server`, when you are trying
to troubleshoot an issue in development.
## Get in touch!
If you're using better_errors, I'd love to hear from you. Drop me a line and tell me what you think!
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request

View File

@ -1,13 +0,0 @@
require "bundler/gem_tasks"
require "rspec/core/rake_task"
namespace :test do
RSpec::Core::RakeTask.new(:with_binding_of_caller)
without_task = RSpec::Core::RakeTask.new(:without_binding_of_caller)
without_task.ruby_opts = "-I spec -r without_binding_of_caller"
task :all => [:with_binding_of_caller, :without_binding_of_caller]
end
task :default => "test:all"

View File

@ -1,27 +0,0 @@
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'better_errors/version'
Gem::Specification.new do |s|
s.name = "better_errors"
s.version = BetterErrors::VERSION
s.authors = ["Charlie Somerville"]
s.email = ["charlie@charliesomerville.com"]
s.description = %q{Provides a better error page for Rails and other Rack apps. Includes source code inspection, a live REPL and local/instance variable inspection for all stack frames.}
s.summary = %q{Better error page for Rails and other Rack apps}
s.homepage = "https://github.com/charliesome/better_errors"
s.license = "MIT"
s.files = `git ls-files`.split($/)
s.test_files = s.files.grep(%r{^(test|spec|features)/})
s.require_paths = ["lib"]
s.required_ruby_version = ">= 2.0.0"
s.add_dependency "erubis", ">= 2.6.6"
s.add_dependency "coderay", ">= 1.0.0"
# optional dependencies:
# s.add_dependency "binding_of_caller"
# s.add_dependency "pry"
end

View File

@ -1,146 +0,0 @@
require "pp"
require "erubis"
require "coderay"
require "uri"
require "better_errors/code_formatter"
require "better_errors/error_page"
require "better_errors/middleware"
require "better_errors/raised_exception"
require "better_errors/repl"
require "better_errors/stack_frame"
require "better_errors/version"
module BetterErrors
POSSIBLE_EDITOR_PRESETS = [
{ symbols: [:emacs, :emacsclient], sniff: /emacs/i, url: "emacs://open?url=file://%{file}&line=%{line}" },
{ symbols: [:macvim, :mvim], sniff: /vim/i, url: proc { |file, line| "mvim://open?url=file://#{file}&line=#{line}" } },
{ symbols: [:sublime, :subl, :st], sniff: /subl/i, url: "subl://open?url=file://%{file}&line=%{line}" },
{ symbols: [:textmate, :txmt, :tm], sniff: /mate/i, url: "txmt://open?url=file://%{file}&line=%{line}" },
]
class << self
# The path to the root of the application. Better Errors uses this property
# to determine if a file in a backtrace should be considered an application
# frame. If you are using Better Errors with Rails, you do not need to set
# this attribute manually.
#
# @return [String]
attr_accessor :application_root
# The logger to use when logging exception details and backtraces. If you
# are using Better Errors with Rails, you do not need to set this attribute
# manually. If this attribute is `nil`, nothing will be logged.
#
# @return [Logger, nil]
attr_accessor :logger
# @private
attr_accessor :binding_of_caller_available
# @private
alias_method :binding_of_caller_available?, :binding_of_caller_available
# The ignored instance variables.
# @return [Array]
attr_accessor :ignored_instance_variables
end
@ignored_instance_variables = []
# Returns a proc, which when called with a filename and line number argument,
# returns a URL to open the filename and line in the selected editor.
#
# Generates TextMate URLs by default.
#
# BetterErrors.editor["/some/file", 123]
# # => txmt://open?url=file:///some/file&line=123
#
# @return [Proc]
def self.editor
@editor
end
# Configures how Better Errors generates open-in-editor URLs.
#
# @overload BetterErrors.editor=(sym)
# Uses one of the preset editor configurations. Valid symbols are:
#
# * `:textmate`, `:txmt`, `:tm`
# * `:sublime`, `:subl`, `:st`
# * `:macvim`
#
# @param [Symbol] sym
#
# @overload BetterErrors.editor=(str)
# Uses `str` as the format string for generating open-in-editor URLs.
#
# Use `%{file}` and `%{line}` as placeholders for the actual values.
#
# @example
# BetterErrors.editor = "my-editor://open?url=%{file}&line=%{line}"
#
# @param [String] str
#
# @overload BetterErrors.editor=(proc)
# Uses `proc` to generate open-in-editor URLs. The proc will be called
# with `file` and `line` parameters when a URL needs to be generated.
#
# Your proc should take care to escape `file` appropriately with
# `URI.encode_www_form_component` (please note that `URI.escape` is **not**
# a suitable substitute.)
#
# @example
# BetterErrors.editor = proc { |file, line|
# "my-editor://open?url=#{URI.encode_www_form_component file}&line=#{line}"
# }
#
# @param [Proc] proc
#
def self.editor=(editor)
POSSIBLE_EDITOR_PRESETS.each do |config|
if config[:symbols].include?(editor)
return self.editor = config[:url]
end
end
if editor.is_a? String
self.editor = proc { |file, line| editor % { file: URI.encode_www_form_component(file), line: line } }
else
if editor.respond_to? :call
@editor = editor
else
raise TypeError, "Expected editor to be a valid editor key, a format string or a callable."
end
end
end
# Enables experimental Pry support in the inline REPL
#
# If you encounter problems while using Pry, *please* file a bug report at
# https://github.com/charliesome/better_errors/issues
def self.use_pry!
REPL::PROVIDERS.unshift const: :Pry, impl: "better_errors/repl/pry"
end
# Automatically sniffs a default editor preset based on the EDITOR
# environment variable.
#
# @return [Symbol]
def self.default_editor
POSSIBLE_EDITOR_PRESETS.detect(-> { {} }) { |config|
ENV["EDITOR"] =~ config[:sniff]
}[:url] || :textmate
end
BetterErrors.editor = default_editor
end
begin
require "binding_of_caller"
require "better_errors/exception_extension"
BetterErrors.binding_of_caller_available = true
rescue LoadError => e
BetterErrors.binding_of_caller_available = false
end
require "better_errors/rails" if defined? Rails::Railtie

View File

@ -1,63 +0,0 @@
module BetterErrors
# @private
class CodeFormatter
require "better_errors/code_formatter/html"
require "better_errors/code_formatter/text"
FILE_TYPES = {
".rb" => :ruby,
"" => :ruby,
".html" => :html,
".erb" => :erb,
".haml" => :haml
}
attr_reader :filename, :line, :context
def initialize(filename, line, context = 5)
@filename = filename
@line = line
@context = context
end
def output
formatted_code
rescue Errno::ENOENT, Errno::EINVAL
source_unavailable
end
def formatted_code
formatted_lines.join
end
def coderay_scanner
ext = File.extname(filename)
FILE_TYPES[ext] || :text
end
def each_line_of(lines, &blk)
line_range.zip(lines).map { |current_line, str|
yield (current_line == line), current_line, str
}
end
def highlighted_lines
CodeRay.scan(context_lines.join, coderay_scanner).div(wrap: nil).lines
end
def context_lines
range = line_range
source_lines[(range.begin - 1)..(range.end - 1)] or raise Errno::EINVAL
end
def source_lines
@source_lines ||= File.readlines(filename)
end
def line_range
min = [line - context, 1].max
max = [line + context, source_lines.count].min
min..max
end
end
end

View File

@ -1,26 +0,0 @@
module BetterErrors
# @private
class CodeFormatter::HTML < CodeFormatter
def source_unavailable
"<p class='unavailable'>Source is not available</p>"
end
def formatted_lines
each_line_of(highlighted_lines) { |highlight, current_line, str|
class_name = highlight ? "highlight" : ""
sprintf '<pre class="%s">%s</pre>', class_name, str
}
end
def formatted_nums
each_line_of(highlighted_lines) { |highlight, current_line, str|
class_name = highlight ? "highlight" : ""
sprintf '<span class="%s">%5d</span>', class_name, current_line
}
end
def formatted_code
%{<div class="code_linenums">#{formatted_nums.join}</div><div class="code">#{super}</div>}
end
end
end

View File

@ -1,14 +0,0 @@
module BetterErrors
# @private
class CodeFormatter::Text < CodeFormatter
def source_unavailable
"# Source is not available"
end
def formatted_lines
each_line_of(context_lines) { |highlight, current_line, str|
sprintf '%s %3d %s', (highlight ? '>' : ' '), current_line, str
}
end
end
end

View File

@ -1,110 +0,0 @@
require "cgi"
require "json"
require "securerandom"
module BetterErrors
# @private
class ErrorPage
def self.template_path(template_name)
File.expand_path("../templates/#{template_name}.erb", __FILE__)
end
def self.template(template_name)
Erubis::EscapedEruby.new(File.read(template_path(template_name)))
end
attr_reader :exception, :env, :repls
def initialize(exception, env)
@exception = RaisedException.new(exception)
@env = env
@start_time = Time.now.to_f
@repls = []
end
def id
@id ||= SecureRandom.hex(8)
end
def render(template_name = "main")
self.class.template(template_name).result binding
end
def do_variables(opts)
index = opts["index"].to_i
@frame = backtrace_frames[index]
@var_start_time = Time.now.to_f
{ html: render("variable_info") }
end
def do_eval(opts)
index = opts["index"].to_i
code = opts["source"]
unless binding = backtrace_frames[index].frame_binding
return { error: "REPL unavailable in this stack frame" }
end
result, prompt, prefilled_input =
(@repls[index] ||= REPL.provider.new(binding)).send_input(code)
{ result: result,
prompt: prompt,
prefilled_input: prefilled_input,
highlighted_input: CodeRay.scan(code, :ruby).div(wrap: nil) }
end
def backtrace_frames
exception.backtrace
end
def application_frames
backtrace_frames.select(&:application?)
end
def first_frame
application_frames.first || backtrace_frames.first
end
private
def editor_url(frame)
BetterErrors.editor[frame.filename, frame.line]
end
def rack_session
env['rack.session']
end
def rails_params
env['action_dispatch.request.parameters']
end
def uri_prefix
env["SCRIPT_NAME"] || ""
end
def request_path
env["PATH_INFO"]
end
def html_formatted_code_block(frame)
CodeFormatter::HTML.new(frame.filename, frame.line).output
end
def text_formatted_code_block(frame)
CodeFormatter::Text.new(frame.filename, frame.line).output
end
def text_heading(char, str)
str + "\n" + char*str.size
end
def inspect_value(obj)
CGI.escapeHTML(obj.inspect)
rescue NoMethodError
"<span class='unsupported'>(object doesn't support inspect)</span>"
rescue Exception => e
"<span class='unsupported'>(exception was raised in inspect)</span>"
end
end
end

View File

@ -1,17 +0,0 @@
module BetterErrors
module ExceptionExtension
prepend_features Exception
def set_backtrace(*)
if caller_locations.none? { |loc| loc.path == __FILE__ }
@__better_errors_bindings_stack = binding.callers.drop(1)
end
super
end
def __better_errors_bindings_stack
@__better_errors_bindings_stack || []
end
end
end

View File

@ -1,141 +0,0 @@
require "json"
require "ipaddr"
require "set"
module BetterErrors
# Better Errors' error handling middleware. Including this in your middleware
# stack will show a Better Errors error page for exceptions raised below this
# middleware.
#
# If you are using Ruby on Rails, you do not need to manually insert this
# middleware into your middleware stack.
#
# @example Sinatra
# require "better_errors"
#
# if development?
# use BetterErrors::Middleware
# end
#
# @example Rack
# require "better_errors"
# if ENV["RACK_ENV"] == "development"
# use BetterErrors::Middleware
# end
#
class Middleware
# The set of IP addresses that are allowed to access Better Errors.
#
# Set to `{ "127.0.0.1/8", "::1/128" }` by default.
ALLOWED_IPS = Set.new
# Adds an address to the set of IP addresses allowed to access Better
# Errors.
def self.allow_ip!(addr)
ALLOWED_IPS << IPAddr.new(addr)
end
allow_ip! "127.0.0.0/8"
allow_ip! "::1/128" rescue nil # windows ruby doesn't have ipv6 support
# A new instance of BetterErrors::Middleware
#
# @param app The Rack app/middleware to wrap with Better Errors
# @param handler The error handler to use.
def initialize(app, handler = ErrorPage)
@app = app
@handler = handler
end
# Calls the Better Errors middleware
#
# @param [Hash] env
# @return [Array]
def call(env)
if allow_ip? env
better_errors_call env
else
@app.call env
end
end
private
def allow_ip?(env)
# REMOTE_ADDR is not in the rack spec, so some application servers do
# not provide it.
return true unless env["REMOTE_ADDR"] and !env["REMOTE_ADDR"].strip.empty?
ip = IPAddr.new env["REMOTE_ADDR"].split("%").first
ALLOWED_IPS.any? { |subnet| subnet.include? ip }
end
def better_errors_call(env)
case env["PATH_INFO"]
when %r{/__better_errors/(?<id>.+?)/(?<method>\w+)\z}
internal_call env, $~
when %r{/__better_errors/?\z}
show_error_page env
else
protected_app_call env
end
end
def protected_app_call(env)
@app.call env
rescue Exception => ex
@error_page = @handler.new ex, env
log_exception
show_error_page(env, ex)
end
def show_error_page(env, exception=nil)
type, content = if @error_page
if text?(env)
[ 'plain', @error_page.render('text') ]
else
[ 'html', @error_page.render ]
end
else
[ 'html', no_errors_page ]
end
status_code = 500
if defined? ActionDispatch::ExceptionWrapper
status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
end
[status_code, { "Content-Type" => "text/#{type}; charset=utf-8" }, [content]]
end
def text?(env)
env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ||
!env["HTTP_ACCEPT"].to_s.include?('html')
end
def log_exception
return unless BetterErrors.logger
message = "\n#{@error_page.exception.type} - #{@error_page.exception.message}:\n"
@error_page.backtrace_frames.each do |frame|
message << " #{frame}\n"
end
BetterErrors.logger.fatal message
end
def internal_call(env, opts)
if opts[:id] != @error_page.id
return [200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(error: "Session expired")]]
end
env["rack.input"].rewind
response = @error_page.send("do_#{opts[:method]}", JSON.parse(env["rack.input"].read))
[200, { "Content-Type" => "text/plain; charset=utf-8" }, [JSON.dump(response)]]
end
def no_errors_page
"<h1>No errors</h1><p>No errors have been recorded yet.</p><hr>" +
"<code>Better Errors v#{BetterErrors::VERSION}</code>"
end
end
end

View File

@ -1,28 +0,0 @@
module BetterErrors
# @private
class Railtie < Rails::Railtie
initializer "better_errors.configure_rails_initialization" do
if use_better_errors?
insert_middleware
BetterErrors.logger = Rails.logger
BetterErrors.application_root = Rails.root.to_s
end
end
def insert_middleware
if defined? ActionDispatch::DebugExceptions
app.middleware.insert_after ActionDispatch::DebugExceptions, BetterErrors::Middleware
else
app.middleware.use BetterErrors::Middleware
end
end
def use_better_errors?
!Rails.env.production? and app.config.consider_all_requests_local
end
def app
Rails.application
end
end
end

View File

@ -1,66 +0,0 @@
# @private
module BetterErrors
class RaisedException
attr_reader :exception, :message, :backtrace
def initialize(exception)
if exception.respond_to?(:original_exception) && exception.original_exception
exception = exception.original_exception
end
@exception = exception
@message = exception.message
setup_backtrace
massage_syntax_error
end
def type
exception.class
end
private
def has_bindings?
exception.respond_to?(:__better_errors_bindings_stack) && exception.__better_errors_bindings_stack.any?
end
def setup_backtrace
if has_bindings?
setup_backtrace_from_bindings
else
setup_backtrace_from_backtrace
end
end
def setup_backtrace_from_bindings
@backtrace = exception.__better_errors_bindings_stack.map { |binding|
file = binding.eval "__FILE__"
line = binding.eval "__LINE__"
name = binding.frame_description
StackFrame.new(file, line, name, binding)
}
end
def setup_backtrace_from_backtrace
@backtrace = (exception.backtrace || []).map { |frame|
if /\A(?<file>.*?):(?<line>\d+)(:in `(?<name>.*)')?/ =~ frame
StackFrame.new(file, line.to_i, name)
end
}.compact
end
def massage_syntax_error
case exception.class.to_s
when "Haml::SyntaxError"
if /\A(.+?):(\d+)/ =~ exception.backtrace.first
backtrace.unshift(StackFrame.new($1, $2.to_i, ""))
end
when "SyntaxError"
if /\A(.+?):(\d+): (.*)/m =~ exception.message
backtrace.unshift(StackFrame.new($1, $2.to_i, ""))
@message = $3
end
end
end
end
end

View File

@ -1,30 +0,0 @@
module BetterErrors
# @private
module REPL
PROVIDERS = [
{ impl: "better_errors/repl/basic",
const: :Basic },
]
def self.provider
@provider ||= const_get detect[:const]
end
def self.provider=(prov)
@provider = prov
end
def self.detect
PROVIDERS.find { |prov|
test_provider prov
}
end
def self.test_provider(provider)
require provider[:impl]
true
rescue LoadError
false
end
end
end

View File

@ -1,20 +0,0 @@
module BetterErrors
module REPL
class Basic
def initialize(binding)
@binding = binding
end
def send_input(str)
[execute(str), ">>", ""]
end
private
def execute(str)
"=> #{@binding.eval(str).inspect}\n"
rescue Exception => e
"!! #{e.inspect rescue e.class.to_s rescue "Exception"}\n"
end
end
end
end

View File

@ -1,78 +0,0 @@
require "fiber"
require "pry"
module BetterErrors
module REPL
class Pry
class Input
def readline
Fiber.yield
end
end
class Output
def initialize
@buffer = ""
end
def puts(*args)
args.each do |arg|
@buffer << "#{arg.chomp}\n"
end
end
def tty?
false
end
def read_buffer
@buffer
ensure
@buffer = ""
end
end
def initialize(binding)
@fiber = Fiber.new do
@pry.repl binding
end
@input = Input.new
@output = Output.new
@pry = ::Pry.new input: @input, output: @output
@pry.hooks.clear_all if defined?(@pry.hooks.clear_all)
@fiber.resume
end
def send_input(str)
local ::Pry.config, color: false, pager: false do
@fiber.resume "#{str}\n"
[@output.read_buffer, *prompt]
end
end
def prompt
if indent = @pry.instance_variable_get(:@indent) and !indent.indent_level.empty?
["..", indent.indent_level]
else
[">>", ""]
end
rescue
[">>", ""]
end
private
def local(obj, attrs)
old_attrs = {}
attrs.each do |k, v|
old_attrs[k] = obj.send k
obj.send "#{k}=", v
end
yield
ensure
old_attrs.each do |k, v|
obj.send "#{k}=", v
end
end
end
end
end

View File

@ -1,111 +0,0 @@
require "set"
module BetterErrors
# @private
class StackFrame
def self.from_exception(exception)
RaisedException.new(exception).backtrace
end
attr_reader :filename, :line, :name, :frame_binding
def initialize(filename, line, name, frame_binding = nil)
@filename = filename
@line = line
@name = name
@frame_binding = frame_binding
set_pretty_method_name if frame_binding
end
def application?
if root = BetterErrors.application_root
filename.index(root) == 0 && filename.index("#{root}/vendor") != 0
end
end
def application_path
filename[(BetterErrors.application_root.length+1)..-1]
end
def gem?
Gem.path.any? { |path| filename.index(path) == 0 }
end
def gem_path
if path = Gem.path.detect { |path| filename.index(path) == 0 }
gem_name_and_version, path = filename.sub("#{path}/gems/", "").split("/", 2)
/(?<gem_name>.+)-(?<gem_version>[\w.]+)/ =~ gem_name_and_version
"#{gem_name} (#{gem_version}) #{path}"
end
end
def class_name
@class_name
end
def method_name
@method_name || @name
end
def context
if gem?
:gem
elsif application?
:application
else
:dunno
end
end
def pretty_path
case context
when :application; application_path
when :gem; gem_path
else filename
end
end
def local_variables
return {} unless frame_binding
frame_binding.eval("local_variables").each_with_object({}) do |name, hash|
if defined?(frame_binding.local_variable_get)
hash[name] = frame_binding.local_variable_get(name)
else
hash[name] = frame_binding.eval(name.to_s)
end
end
end
def instance_variables
return {} unless frame_binding
Hash[visible_instance_variables.map { |x|
[x, frame_binding.eval(x.to_s)]
}]
end
def visible_instance_variables
frame_binding.eval("instance_variables") - BetterErrors.ignored_instance_variables
end
def to_s
"#{pretty_path}:#{line}:in `#{name}'"
end
private
def set_pretty_method_name
name =~ /\A(block (\([^)]+\) )?in )?/
recv = frame_binding.eval("self")
return unless method_name = frame_binding.eval("::Kernel.__method__")
if Module === recv
@class_name = "#{$1}#{recv}"
@method_name = ".#{method_name}"
else
@class_name = "#{$1}#{Kernel.instance_method(:class).bind(recv).call}"
@method_name = "##{method_name}"
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
<%== text_heading("=", "%s at %s" % [exception.type, request_path]) %>
> <%== exception.message %>
<% if backtrace_frames.any? %>
<%== text_heading("-", "%s, line %i" % [first_frame.pretty_path, first_frame.line]) %>
``` ruby
<%== text_formatted_code_block(first_frame) %>```
App backtrace
-------------
<%== application_frames.map { |s| " - #{s}" }.join("\n") %>
Full backtrace
--------------
<%== backtrace_frames.map { |s| " - #{s}" }.join("\n") %>
<% end %>

View File

@ -1,70 +0,0 @@
<header class="trace_info clearfix">
<div class="title">
<h2 class="name"><%= @frame.name %></h2>
<div class="location"><span class="filename"><a href="<%= editor_url(@frame) %>"><%= @frame.pretty_path %></a></span></div>
</div>
<div class="code_block clearfix">
<%== html_formatted_code_block @frame %>
</div>
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
<div class="repl">
<div class="console">
<pre></pre>
<div class="prompt"><span>&gt;&gt;</span> <input/></div>
</div>
</div>
<% end %>
</header>
<% if BetterErrors.binding_of_caller_available? && @frame.frame_binding %>
<div class="hint">
This is a live shell. Type in here.
</div>
<div class="variable_info"></div>
<% end %>
<% unless BetterErrors.binding_of_caller_available? %>
<div class="hint">
<strong>Tip:</strong> add <code>gem "binding_of_caller"</code> to your Gemfile to enable the REPL and local/instance variable inspection.
</div>
<% end %>
<div class="sub">
<h3>Request info</h3>
<div class='inset variables'>
<table class="var_table">
<% if rails_params %>
<tr><td class="name">Request parameters</td><td><pre><%== inspect_value rails_params %></pre></td></tr>
<% end %>
<% if rack_session %>
<tr><td class="name">Rack session</td><td><pre><%== inspect_value rack_session %></pre></td></tr>
<% end %>
</table>
</div>
</div>
<div class="sub">
<h3>Local Variables</h3>
<div class='inset variables'>
<table class="var_table">
<% @frame.local_variables.each do |name, value| %>
<tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
<% end %>
</table>
</div>
</div>
<div class="sub">
<h3>Instance Variables</h3>
<div class="inset variables">
<table class="var_table">
<% @frame.instance_variables.each do |name, value| %>
<tr><td class="name"><%= name %></td><td><pre><%== inspect_value value %></pre></td></tr>
<% end %>
</table>
</div>
</div>
<!-- <%= Time.now.to_f - @var_start_time %> seconds -->

View File

@ -1,3 +0,0 @@
module BetterErrors
VERSION = "1.1.0"
end

View File

@ -1,92 +0,0 @@
require "spec_helper"
module BetterErrors
describe CodeFormatter do
let(:filename) { File.expand_path("../support/my_source.rb", __FILE__) }
let(:formatter) { CodeFormatter.new(filename, 8) }
it "picks an appropriate scanner" do
formatter.coderay_scanner.should == :ruby
end
it "shows 5 lines of context" do
formatter.line_range.should == (3..13)
formatter.context_lines.should == [
"three\n",
"four\n",
"five\n",
"six\n",
"seven\n",
"eight\n",
"nine\n",
"ten\n",
"eleven\n",
"twelve\n",
"thirteen\n"
]
end
it "works when the line is right on the edge" do
formatter = CodeFormatter.new(filename, 20)
formatter.line_range.should == (15..20)
end
describe CodeFormatter::HTML do
it "highlights the erroring line" do
formatter = CodeFormatter::HTML.new(filename, 8)
formatter.output.should =~ /highlight.*eight/
end
it "works when the line is right on the edge" do
formatter = CodeFormatter::HTML.new(filename, 20)
formatter.output.should_not == formatter.source_unavailable
end
it "doesn't barf when the lines don't make any sense" do
formatter = CodeFormatter::HTML.new(filename, 999)
formatter.output.should == formatter.source_unavailable
end
it "doesn't barf when the file doesn't exist" do
formatter = CodeFormatter::HTML.new("fkdguhskd7e l", 1)
formatter.output.should == formatter.source_unavailable
end
end
describe CodeFormatter::Text do
it "highlights the erroring line" do
formatter = CodeFormatter::Text.new(filename, 8)
formatter.output.should == <<-TEXT.gsub(/^ /, "")
3 three
4 four
5 five
6 six
7 seven
> 8 eight
9 nine
10 ten
11 eleven
12 twelve
13 thirteen
TEXT
end
it "works when the line is right on the edge" do
formatter = CodeFormatter::Text.new(filename, 20)
formatter.output.should_not == formatter.source_unavailable
end
it "doesn't barf when the lines don't make any sense" do
formatter = CodeFormatter::Text.new(filename, 999)
formatter.output.should == formatter.source_unavailable
end
it "doesn't barf when the file doesn't exist" do
formatter = CodeFormatter::Text.new("fkdguhskd7e l", 1)
formatter.output.should == formatter.source_unavailable
end
end
end
end

View File

@ -1,76 +0,0 @@
require "spec_helper"
module BetterErrors
describe ErrorPage do
let!(:exception) { raise ZeroDivisionError, "you divided by zero you silly goose!" rescue $! }
let(:error_page) { ErrorPage.new exception, { "PATH_INFO" => "/some/path" } }
let(:response) { error_page.render }
let(:empty_binding) {
local_a = :value_for_local_a
local_b = :value_for_local_b
@inst_c = :value_for_inst_c
@inst_d = :value_for_inst_d
binding
}
it "includes the error message" do
response.should include("you divided by zero you silly goose!")
end
it "includes the request path" do
response.should include("/some/path")
end
it "includes the exception class" do
response.should include("ZeroDivisionError")
end
context "variable inspection" do
let(:exception) { empty_binding.eval("raise") rescue $! }
if BetterErrors.binding_of_caller_available?
it "shows local variables" do
html = error_page.do_variables("index" => 0)[:html]
html.should include("local_a")
html.should include(":value_for_local_a")
html.should include("local_b")
html.should include(":value_for_local_b")
end
else
it "tells the user to add binding_of_caller to their gemfile to get fancy features" do
html = error_page.do_variables("index" => 0)[:html]
html.should include(%{gem "binding_of_caller"})
end
end
it "shows instance variables" do
html = error_page.do_variables("index" => 0)[:html]
html.should include("inst_c")
html.should include(":value_for_inst_c")
html.should include("inst_d")
html.should include(":value_for_inst_d")
end
it "shows filter instance variables" do
BetterErrors.stub(:ignored_instance_variables).and_return([ :@inst_d ])
html = error_page.do_variables("index" => 0)[:html]
html.should include("inst_c")
html.should include(":value_for_inst_c")
html.should_not include('<td class="name">@inst_d</td>')
html.should_not include("<pre>:value_for_inst_d</pre>")
end
end
it "doesn't die if the source file is not a real filename" do
exception.stub(:backtrace).and_return([
"<internal:prelude>:10:in `spawn_rack_application'"
])
response.should include("Source unavailable")
end
end
end

View File

@ -1,146 +0,0 @@
require "spec_helper"
module BetterErrors
describe Middleware do
let(:app) { Middleware.new(->env { ":)" }) }
let(:exception) { RuntimeError.new("oh no :(") }
it "passes non-error responses through" do
app.call({}).should == ":)"
end
it "calls the internal methods" do
app.should_receive :internal_call
app.call("PATH_INFO" => "/__better_errors/1/preform_awesomness")
end
it "calls the internal methods on any subfolder path" do
app.should_receive :internal_call
app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors/1/preform_awesomness")
end
it "shows the error page" do
app.should_receive :show_error_page
app.call("PATH_INFO" => "/__better_errors/")
end
it "shows the error page on any subfolder path" do
app.should_receive :show_error_page
app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors/")
end
it "doesn't show the error page to a non-local address" do
app.should_not_receive :better_errors_call
app.call("REMOTE_ADDR" => "1.2.3.4")
end
it "shows to a whitelisted IP" do
BetterErrors::Middleware.allow_ip! '77.55.33.11'
app.should_receive :better_errors_call
app.call("REMOTE_ADDR" => "77.55.33.11")
end
it "doesn't blow up when given a blank REMOTE_ADDR" do
expect { app.call("REMOTE_ADDR" => " ") }.to_not raise_error
end
it "doesn't blow up when given an IP address with a zone index" do
expect { app.call("REMOTE_ADDR" => "0:0:0:0:0:0:0:1%0" ) }.to_not raise_error
end
context "when requesting the /__better_errors manually" do
let(:app) { Middleware.new(->env { ":)" }) }
it "shows that no errors have been recorded" do
status, headers, body = app.call("PATH_INFO" => "/__better_errors")
body.join.should match /No errors have been recorded yet./
end
it "shows that no errors have been recorded on any subfolder path" do
status, headers, body = app.call("PATH_INFO" => "/any_sub/folder/path/__better_errors")
body.join.should match /No errors have been recorded yet./
end
end
context "when handling an error" do
let(:app) { Middleware.new(->env { raise exception }) }
it "returns status 500" do
status, headers, body = app.call({})
status.should == 500
end
context "original_exception" do
class OriginalExceptionException < Exception
attr_reader :original_exception
def initialize(message, original_exception = nil)
super(message)
@original_exception = original_exception
end
end
it "shows Original Exception if it responds_to and has an original_exception" do
app = Middleware.new(->env {
raise OriginalExceptionException.new("Other Exception", Exception.new("Original Exception"))
})
status, _, body = app.call({})
status.should == 500
body.join.should_not match(/Other Exception/)
body.join.should match(/Original Exception/)
end
it "won't crash if the exception responds_to but doesn't have an original_exception" do
app = Middleware.new(->env {
raise OriginalExceptionException.new("Other Exception")
})
status, _, body = app.call({})
status.should == 500
body.join.should match(/Other Exception/)
end
end
it "returns ExceptionWrapper's status_code" do
ad_ew = double("ActionDispatch::ExceptionWrapper")
ad_ew.stub('new').with({}, exception ){ double("ExceptionWrapper", status_code: 404) }
stub_const('ActionDispatch::ExceptionWrapper', ad_ew)
status, headers, body = app.call({})
status.should == 404
end
it "returns UTF-8 error pages" do
status, headers, body = app.call({})
headers["Content-Type"].should match /charset=utf-8/
end
it "returns text pages by default" do
status, headers, body = app.call({})
headers["Content-Type"].should match /text\/plain/
end
it "returns HTML pages by default" do
# Chrome's 'Accept' header looks similar this.
status, headers, body = app.call("HTTP_ACCEPT" => "text/html,application/xhtml+xml;q=0.9,*/*")
headers["Content-Type"].should match /text\/html/
end
it "logs the exception" do
logger = Object.new
logger.should_receive :fatal
BetterErrors.stub(:logger).and_return(logger)
app.call({})
end
end
end
end

View File

@ -1,52 +0,0 @@
require "spec_helper"
module BetterErrors
describe RaisedException do
let(:exception) { RuntimeError.new("whoops") }
subject { RaisedException.new(exception) }
its(:exception) { should == exception }
its(:message) { should == "whoops" }
its(:type) { should == RuntimeError }
context "when the exception wraps another exception" do
let(:original_exception) { RuntimeError.new("something went wrong!") }
let(:exception) { double(:original_exception => original_exception) }
its(:exception) { should == original_exception }
its(:message) { should == "something went wrong!" }
end
context "when the exception is a syntax error" do
let(:exception) { SyntaxError.new("foo.rb:123: you made a typo!") }
its(:message) { should == "you made a typo!" }
its(:type) { should == SyntaxError }
it "has the right filename and line number in the backtrace" do
subject.backtrace.first.filename.should == "foo.rb"
subject.backtrace.first.line.should == 123
end
end
context "when the exception is a HAML syntax error" do
before do
stub_const("Haml::SyntaxError", Class.new(SyntaxError))
end
let(:exception) {
Haml::SyntaxError.new("you made a typo!").tap do |ex|
ex.set_backtrace(["foo.rb:123", "haml/internals/blah.rb:123456"])
end
}
its(:message) { should == "you made a typo!" }
its(:type) { should == Haml::SyntaxError }
it "has the right filename and line number in the backtrace" do
subject.backtrace.first.filename.should == "foo.rb"
subject.backtrace.first.line.should == 123
end
end
end
end

View File

@ -1,18 +0,0 @@
require "spec_helper"
require "better_errors/repl/basic"
require "better_errors/repl/shared_examples"
module BetterErrors
module REPL
describe Basic do
let(:fresh_binding) {
local_a = 123
binding
}
let(:repl) { Basic.new fresh_binding }
it_behaves_like "a REPL provider"
end
end
end

View File

@ -1,40 +0,0 @@
require "spec_helper"
require "pry"
require "better_errors/repl/pry"
require "better_errors/repl/shared_examples"
module BetterErrors
module REPL
describe Pry do
let(:fresh_binding) {
local_a = 123
binding
}
let(:repl) { Pry.new fresh_binding }
it "does line continuation" do
output, prompt, filled = repl.send_input ""
output.should == "=> nil\n"
prompt.should == ">>"
filled.should == ""
output, prompt, filled = repl.send_input "def f(x)"
output.should == ""
prompt.should == ".."
filled.should == " "
output, prompt, filled = repl.send_input "end"
if RUBY_VERSION >= "2.1.0"
output.should == "=> :f\n"
else
output.should == "=> nil\n"
end
prompt.should == ">>"
filled.should == ""
end
it_behaves_like "a REPL provider"
end
end
end

View File

@ -1,18 +0,0 @@
shared_examples_for "a REPL provider" do
it "evaluates ruby code in a given context" do
repl.send_input("local_a = 456")
fresh_binding.eval("local_a").should == 456
end
it "returns a tuple of output and the new prompt" do
output, prompt = repl.send_input("1 + 2")
output.should == "=> 3\n"
prompt.should == ">>"
end
it "doesn't barf if the code throws an exception" do
output, prompt = repl.send_input("raise Exception")
output.should include "Exception: Exception"
prompt.should == ">>"
end
end

View File

@ -1,157 +0,0 @@
require "spec_helper"
module BetterErrors
describe StackFrame do
context "#application?" do
it "is true for application filenames" do
BetterErrors.stub(:application_root).and_return("/abc/xyz")
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
frame.application?.should be_true
end
it "is false for everything else" do
BetterErrors.stub(:application_root).and_return("/abc/xyz")
frame = StackFrame.new("/abc/nope", 123, "foo")
frame.application?.should be_false
end
it "doesn't care if no application_root is set" do
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
frame.application?.should be_false
end
end
context "#gem?" do
it "is true for gem filenames" do
Gem.stub(:path).and_return(["/abc/xyz"])
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
frame.gem?.should be_true
end
it "is false for everything else" do
Gem.stub(:path).and_return(["/abc/xyz"])
frame = StackFrame.new("/abc/nope", 123, "foo")
frame.gem?.should be_false
end
end
context "#application_path" do
it "chops off the application root" do
BetterErrors.stub(:application_root).and_return("/abc/xyz")
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
frame.application_path.should == "app/controllers/crap_controller.rb"
end
end
context "#gem_path" do
it "chops of the gem path and stick (gem) there" do
Gem.stub(:path).and_return(["/abc/xyz"])
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
frame.gem_path.should == "whatever (1.2.3) lib/whatever.rb"
end
it "prioritizes gem path over application path" do
BetterErrors.stub(:application_root).and_return("/abc/xyz")
Gem.stub(:path).and_return(["/abc/xyz/vendor"])
frame = StackFrame.new("/abc/xyz/vendor/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
frame.gem_path.should == "whatever (1.2.3) lib/whatever.rb"
end
end
context "#pretty_path" do
it "returns #application_path for application paths" do
BetterErrors.stub(:application_root).and_return("/abc/xyz")
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index")
frame.pretty_path.should == frame.application_path
end
it "returns #gem_path for gem paths" do
Gem.stub(:path).and_return(["/abc/xyz"])
frame = StackFrame.new("/abc/xyz/gems/whatever-1.2.3/lib/whatever.rb", 123, "foo")
frame.pretty_path.should == frame.gem_path
end
end
it "special cases SyntaxErrors" do
begin
eval(%{ raise SyntaxError, "you wrote bad ruby!" }, nil, "my_file.rb", 123)
rescue SyntaxError => syntax_error
end
frames = StackFrame.from_exception(syntax_error)
frames.first.filename.should == "my_file.rb"
frames.first.line.should == 123
end
it "doesn't blow up if no method name is given" do
error = StandardError.allocate
error.stub(:backtrace).and_return(["foo.rb:123"])
frames = StackFrame.from_exception(error)
frames.first.filename.should == "foo.rb"
frames.first.line.should == 123
error.stub(:backtrace).and_return(["foo.rb:123: this is an error message"])
frames = StackFrame.from_exception(error)
frames.first.filename.should == "foo.rb"
frames.first.line.should == 123
end
it "ignores a backtrace line if its format doesn't make any sense at all" do
error = StandardError.allocate
error.stub(:backtrace).and_return(["foo.rb:123:in `foo'", "C:in `find'", "bar.rb:123:in `bar'"])
frames = StackFrame.from_exception(error)
frames.count.should == 2
end
it "doesn't blow up if a filename contains a colon" do
error = StandardError.allocate
error.stub(:backtrace).and_return(["crap:filename.rb:123"])
frames = StackFrame.from_exception(error)
frames.first.filename.should == "crap:filename.rb"
end
it "doesn't blow up with a BasicObject as frame binding" do
obj = BasicObject.new
def obj.my_binding
::Kernel.binding
end
frame = StackFrame.new("/abc/xyz/app/controllers/crap_controller.rb", 123, "index", obj.my_binding)
frame.class_name.should == 'BasicObject'
end
it "sets method names properly" do
obj = "string"
def obj.my_method
begin
raise "foo"
rescue => err
err
end
end
frame = StackFrame.from_exception(obj.my_method).first
if BetterErrors.binding_of_caller_available?
frame.method_name.should == "#my_method"
frame.class_name.should == "String"
else
frame.method_name.should == "my_method"
frame.class_name.should == nil
end
end
if RUBY_ENGINE == "java"
it "doesn't blow up on a native Java exception" do
expect { StackFrame.from_exception(java.lang.Exception.new) }.to_not raise_error
end
end
end
end

View File

@ -1,20 +0,0 @@
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
eighteen
nineteen
twenty

View File

@ -1,73 +0,0 @@
require "spec_helper"
describe BetterErrors do
context ".editor" do
it "defaults to textmate" do
subject.editor["foo.rb", 123].should == "txmt://open?url=file://foo.rb&line=123"
end
it "url escapes the filename" do
subject.editor["&.rb", 0].should == "txmt://open?url=file://%26.rb&line=0"
end
[:emacs, :emacsclient].each do |editor|
it "uses emacs:// scheme when set to #{editor.inspect}" do
subject.editor = editor
subject.editor[].should start_with "emacs://"
end
end
[:macvim, :mvim].each do |editor|
it "uses mvim:// scheme when set to #{editor.inspect}" do
subject.editor = editor
subject.editor[].should start_with "mvim://"
end
end
[:sublime, :subl, :st].each do |editor|
it "uses subl:// scheme when set to #{editor.inspect}" do
subject.editor = editor
subject.editor[].should start_with "subl://"
end
end
[:textmate, :txmt, :tm].each do |editor|
it "uses txmt:// scheme when set to #{editor.inspect}" do
subject.editor = editor
subject.editor[].should start_with "txmt://"
end
end
["emacsclient", "/usr/local/bin/emacsclient"].each do |editor|
it "uses emacs:// scheme when EDITOR=#{editor}" do
ENV["EDITOR"] = editor
subject.editor = subject.default_editor
subject.editor[].should start_with "emacs://"
end
end
["mvim -f", "/usr/local/bin/mvim -f"].each do |editor|
it "uses mvim:// scheme when EDITOR=#{editor}" do
ENV["EDITOR"] = editor
subject.editor = subject.default_editor
subject.editor[].should start_with "mvim://"
end
end
["subl -w", "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl"].each do |editor|
it "uses mvim:// scheme when EDITOR=#{editor}" do
ENV["EDITOR"] = editor
subject.editor = subject.default_editor
subject.editor[].should start_with "subl://"
end
end
["mate -w", "/usr/bin/mate -w"].each do |editor|
it "uses txmt:// scheme when EDITOR=#{editor}" do
ENV["EDITOR"] = editor
subject.editor = subject.default_editor
subject.editor[].should start_with "txmt://"
end
end
end
end

View File

@ -1,5 +0,0 @@
$: << File.expand_path("../../lib", __FILE__)
ENV["EDITOR"] = nil
require "better_errors"

View File

@ -1,9 +0,0 @@
module Kernel
alias_method :require_with_binding_of_caller, :require
def require(feature)
raise LoadError if feature == "binding_of_caller"
require_with_binding_of_caller(feature)
end
end

View File

@ -0,0 +1,30 @@
#coding=utf-8
#!/usr/bin/env python
# 脚本用于刷新版本库由git hooks里进行调用传入参数为git仓库路径
# 需要配置rails项目地址
# 必须装此文件放在git的存放目录现在是 /home/pdl/redmine-2.3.2-0/apache2/htdocs
import sys
import os
import urllib
import urllib2
RAILS_URL = 'http://192.168.128.128:3000/'
def get_git_path():
return sys.argv[1]
path=os.path.realpath(sys.argv[0])
if os.path.isfile(path):
path=os.path.dirname(os.path.dirname(path))
return os.path.abspath(path)
def post_http_data(url, data):
data_urlencode = urllib.urlencode(data)
req = urllib2.Request(url = url,data =data_urlencode)
res_data = urllib2.urlopen(req)
#res = res_data.read()
#return res
path = get_git_path()
post_http_data(RAILS_URL + 'git_callback/post_update', {'root_url': path})

View File

@ -1,9 +0,0 @@
language: ruby
rvm:
- 1.9.3
- 2.0.0
- 2.1.1
bundler_args: ""
services:
- redis
- memcached

View File

@ -1,181 +0,0 @@
28-June-2012 - Sam
* Started change log
* Corrected profiler so it properly captures POST requests (was supressing non 200s)
* Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
* Fixed bug where unviewed missing ids never got cleared
* Supress all '/assets/' in the rails tie (makes debugging easier)
* record_sql was mega buggy
* added MemcacheStore
9-July-2012 - Sam
* Cleaned up mechanism for profiling in production, all you need to do now
is call Rack::MiniProfiler.authorize_request to get profiling working in
production
* Added option to display full backtraces pp=full-backtrace
* Cleaned up railties, got rid of the post authorize callback
* Version 0.1.3
12-July-2012 - Sam
* Fixed incorrect profiling steps (was not indenting or measuring start time right
* Implemented native PG and MySql2 interceptors, this gives way more accurate times
* Refactored context so its a proper class and not a hash
* Added some more client probing built in to rails
* More tests
18-July-2012 - Sam
* Added First Paint time for chrome
* Bug fix to ensure non Rails installs have mini profiler
* Version 0.1.7
30-July-2012 - Sam
* Made compliant with ancient versions of Rack (including Rack used by Rails2)
* Fixed broken share link
* Fixed crashes on startup (in MemoryStore and FileStore)
* Version 0.1.8
* Unicode fix
* Version 0.1.9
7-August-2012 - Sam
* Added option to disable profiler for the current session (pp=disable / pp=enable)
* yajl compatability contributed by Sven Riedel
10-August-2012 - Sam
* Added basic prepared statement profiling for postgres
20-August-2012 - Sam
* 1.12.pre
* Cap X-MiniProfiler-Ids at 10, otherwise the header can get killed
3-September-2012 - Sam
* 1.13.pre
* pg gem prepared statements were not being logged correctly
* added setting config.backtrace_ignores = [] - an array of regexes that match on caller lines that get ignored
* added setting config.backtrace_includes = [] - an array of regexes that get included in the trace by default
* cleaned up the way client settings are stored
* made pp=full-backtrace "sticky"
* added pp=normal-backtrace to clear the "sticky" state
* change "pp=sample" to work with "caller" no need for stack trace gem
4-September-2012 - Sam
* 1.15.pre
* fixed annoying bug where client settings were not sticking
* fixed long standing issue with Rack::ConditionalGet stopping MiniProfiler from working properly
5-September-2012 - Sam
* 1.16
* fixed long standing problem specs (issue with memory store)
* fixed issue where profiler would be dumped when you got a 404 in production (and any time rails is bypassed)
* implemented stacktrace properly
9-September-2012 - Sam
* 1.17
* pp=sample was bust unless stacktrace was installed
10-September-2012 - Sam
* 1.19
* fix compat issue with 1.8.7
12-September-2012 - Sam
* 1.20
* Added pp=profile-gc , it allows you to profile the GC in Ruby 1.9.3
17-September-2012
* 1.21
* New MemchacedStore
* Rails 4 support
17-September-2012
* Allow rack-mini-profiler to be sourced from github
* Extracted the pp=profile-gc-time out, the object space profiler needs to disable gc
20-September-2012
* 1.22
* Fix permission issue in the gem
8-April-2013
* 1.24
* Flame Graph Support see: http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler
* Fix file retention leak in file_store
* New toggle_shortcut and start_hidden options
* Fix for AngularJS support and MooTools
* More robust gc profiling
* Mongoid support
* Fix for html5 implicit body tags
* script tag initialized via data-attributes
* new - Rack::MiniProfiler.counter counter_name {}
* Allow usage of existing jQuery if its already loaded
* Fix pp=enable
* 1.8.7 support ... grrr
* Net:HTTP profiling
* pre authorize to run in all non development? and production? modes
8-April-2013
* 1.25
* Missed flamegraph.html from build
11-April-2013
* 1.26
* (minor) allow Rack::MiniProfilerRails.initialize!(Rails.application), for post config intialization
26-June-2013
* 1.27
* Disable global ajax handlers on MP requests @JP
* Add Rack::MiniProfiler.config.backtrace_threshold_ms
* jQuery 2.0 support
18-July-2013
* 1.28
* diagnostics in abstract storage was raising not implemented killing
?pp=env and others
* SOLR xml unescaped by mistake
20-August-2013
* 1.29
* Bugfix: SOLR patching had an incorrect monkey patch
* Implemented exception tracing using TracePoint see pp=trace-exceptions
30-August-2013
* 1.30
* Feature: Added Rack::MiniProfiler.counter_method(klass,name) for injecting counters
* Bug: Counters were not shifting the table correctly
3-September-2013
* Ripped out flamegraph so it can be isolated into a gem
* Flamegraph now has much increased fidelity
* Ripped out pp=sample it just was never really used
17-September-2013 - Ross Wilson
* Instead of supressing all "/assets/" requests we now check the configured
config.assets.prefix path since developers can rename the path to serve Asset Pipeline
files from
12-December-2013 - Sam Saffron
* Version 0.9.0.pre (bumped up to reflect the stability of the project)
* Improved reports for pp=profile-gc
* pp=flamegraph&flamegraph_sample_rate=1 , allow you to specify sampling rates
13-March-2014 - Sam Saffron
* Version 0.9.1
* Added back Ruby 1.8 support (thanks Malet)
* Corrected Rails 3.0 support (thanks Zlatko)
* Corrected fix possible XSS (admin only)
* Amend Railstie so MiniProfiler can be launched with action view or action controller (Thanks Akira)
* Corrected Sql patching to avoid setting instance vars on nil which is frozen (thanks Andy, huoxito)

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