Merge remote-tracking branch 'origin/szzh' into szzh
This commit is contained in:
commit
cdad43fdfc
|
@ -22,3 +22,4 @@
|
|||
.DS_Store
|
||||
public/api_doc/
|
||||
/.metadata
|
||||
vendor/cache
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
20
Gemfile
20
Gemfile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -1994,4 +1994,10 @@ module ApplicationHelper
|
|||
end
|
||||
technical_title
|
||||
end
|
||||
|
||||
|
||||
def ie8?
|
||||
request.env["HTTP_USER_AGENT"] =~ /MSIE 8.0/
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
#课程留言列表
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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>"+"个文件");
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" %>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 %>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %>
|
|
@ -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>
|
||||
|
|
|
@ -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) %>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -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上传头像模块
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
@ -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: 请输入邮箱地址
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: 需求
|
||||
|
|
|
@ -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: 请输入邮箱地址
|
||||
|
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +0,0 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
- 2.1.0
|
||||
- 2.0.0
|
|
@ -1 +0,0 @@
|
|||
--markup markdown --no-private
|
|
@ -1,3 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
See https://github.com/charliesome/better_errors/releases
|
|
@ -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"
|
|
@ -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.
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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 %>
|
|
@ -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>>></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 -->
|
|
@ -1,3 +0,0 @@
|
|||
module BetterErrors
|
||||
VERSION = "1.1.0"
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,20 +0,0 @@
|
|||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
eight
|
||||
nine
|
||||
ten
|
||||
eleven
|
||||
twelve
|
||||
thirteen
|
||||
fourteen
|
||||
fifteen
|
||||
sixteen
|
||||
seventeen
|
||||
eighteen
|
||||
nineteen
|
||||
twenty
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
$: << File.expand_path("../../lib", __FILE__)
|
||||
|
||||
ENV["EDITOR"] = nil
|
||||
|
||||
require "better_errors"
|
|
@ -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
|
|
@ -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})
|
|
@ -1,9 +0,0 @@
|
|||
language: ruby
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
- 2.1.1
|
||||
bundler_args: ""
|
||||
services:
|
||||
- redis
|
||||
- memcached
|
|
@ -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
Loading…
Reference in New Issue