Merge branch 'develop' of https://git.trustie.net/jacknudt/trustieforge into develop

This commit is contained in:
cxt 2016-04-09 18:35:07 +08:00
commit 5e3e2e64a9
115 changed files with 9416 additions and 5279 deletions

1
1234567 Normal file
View File

@ -0,0 +1 @@
{"access_token":"7MBMEBoE6sSC15bIHZYAZSxj47yCKlbWEVjrkUgEJxPP3K083tbhc1RIWmxGu3WoB5dAXxK_yd4l1jrcvt6YrsTcOfFGRirOHVfzrpvhsQgxOoxcdc7YljfO_dnwUtWgFTAcAIALZG","expires_in":7200,"got_token_at":1460189856}

View File

@ -6,6 +6,7 @@ unless RUBY_PLATFORM =~ /w32/
gem 'iconv'
end
gem 'wechat',git: 'https://github.com/guange2015/wechat.git'
gem 'grack', path:'lib/grack'
gem 'gitlab', path: 'lib/gitlab-cli'
gem 'rest-client'
@ -16,8 +17,9 @@ gem 'delayed_job_active_record'#, :group => :production
gem 'daemons'
gem 'grape', '~> 0.9.0'
gem 'grape-entity'
gem 'rack-cors', :require => 'rack/cors'
gem 'seems_rateable', '~> 1.0.13'
gem "rails", "~> 3.2.13"
gem 'rails', '~> 3.2'
gem "jquery-rails", "~> 2.0.2"
gem "i18n", "~> 0.6.0"
gem 'coderay', '~> 1.1.0'
@ -43,7 +45,7 @@ gem 'elasticsearch-rails'
group :development do
gem 'grape-swagger'
gem 'better_errors', '~> 1.1.0'
gem 'rack-mini-profiler', '~> 0.9.3'
# gem 'rack-mini-profiler', '~> 0.9.3'
if RUBY_PLATFORM =~ /w32/
gem 'win32console'
end

View File

@ -7,6 +7,16 @@ module Mobile
require_relative 'apis/upgrade'
require_relative 'apis/homeworks'
require_relative 'apis/comments'
require_relative 'apis/issues'
require_relative 'apis/activities'
require_relative 'apis/whomeworks'
require_relative 'apis/newss'
require_relative 'apis/journal_for_messages'
require_relative 'apis/messages'
require_relative 'apis/blog_comments'
require_relative 'apis/new_comment'
require_relative 'apis/praise'
class API < Grape::API
version 'v1', using: :path
format :json
@ -39,6 +49,15 @@ module Mobile
mount Apis::Upgrade
mount Apis::Homeworks
mount Apis::Comments
mount Apis::Issues
mount Apis::Activities
mount Apis::Whomeworks
mount Apis::Newss
mount Apis::JournalForMessages
mount Apis::Messages
mount Apis::BlogComments
mount Apis::NewComment
mount Apis::Praise
#add_swagger_documentation ({api_version: 'v1', base_path: 'http://u06.shellinfo.cn/trustie/api'})
#add_swagger_documentation ({api_version: 'v1', base_path: '/api'}) if Rails.env.development?

View File

@ -0,0 +1,63 @@
#coding=utf-8
module Mobile
module Apis
class Activities< Grape::API
resources :activities do
desc "get user activities"
params do
requires :page, type: Integer
requires :openid, type: String
end
post do
user = UserWechat.find_by_openid(params[:openid]).user
=begin
shield_project_ids = ShieldActivity.where("container_type='User' and container_id=#{user.id} and shield_type='Project'").map(&:shield_id)
shield_course_ids = ShieldActivity.where("container_type='User' and container_id=#{user.id} and shield_type='Course'").map(&:shield_id)
page = params[:page] ? params[:page] : 0
user_project_ids = (user.projects.visible.map{|project| project.id}-shield_project_ids).empty? ? "(-1)" : "(" + (user.projects.visible.map{|project| project.id}-shield_project_ids).join(",") + ")"
user_course_ids = (user.courses.visible.map{|course| course.id}-shield_course_ids).empty? ? "(-1)" : "(" + (user.courses.visible.map{|course| course.id}-shield_course_ids).join(",") + ")"
course_types = "('Message','News','HomeworkCommon','Poll','Course')"
project_types = "('Message','Issue','ProjectCreateInfo')"
principal_types = "JournalsForMessage"
blog_ids = "("+user.blog.id.to_s+","+((User.watched_by(user.id).count == 0 )? '0' :User.watched_by(user.id).map{|u| u.blog.id}.join(','))+")"
activities = UserActivity.where("(container_type = 'Project' and container_id in #{user_project_ids} and act_type in #{project_types})" +
"or (container_type = 'Course' and container_id in #{user_course_ids} and act_type in #{course_types}) "+
"or (container_type = 'Principal' and act_type= '#{principal_types}' and container_id = #{user.id}) " +
"or (container_type = 'Blog' and act_type= 'BlogComment' and container_id in #{blog_ids})").order('updated_at desc')
=end
shield_project_ids = ShieldActivity.select("shield_id").where("container_type='User' and container_id=#{user.id} and shield_type='Project'").map(&:shield_id)
shield_course_ids = ShieldActivity.select("shield_id").where("container_type='User' and container_id=#{user.id} and shield_type='Course'").map(&:shield_id)
page = params[:page] ? params[:page] : 0
user_project_ids = (user.projects.map{|project| project.id}-shield_project_ids).empty? ? "(-1)" : "(" + (user.projects.map{|project| project.id}-shield_project_ids).join(",") + ")"
user_course_ids = (user.courses.map{|course| course.id}-shield_course_ids).empty? ? "(-1)" : "(" + (user.courses.map{|course| course.id}-shield_course_ids).join(",") + ")"
course_types = "('Message','News','HomeworkCommon','Poll','Course')"
project_types = "('Message','Issue','ProjectCreateInfo')"
principal_types = "JournalsForMessage"
watched_user_ids = User.watched_by(user.id).count == 0 ? " " : ("," + User.watched_by(user.id).map{|u| u.id.to_s }.join(','))
user_ids = "(" + user.id.to_s + watched_user_ids + ")"
watched_user_blog_ids = Blog.select("id").where("author_id in #{user_ids}").map { |blog| blog.id}.join(",")
blog_ids = "(" + watched_user_blog_ids + ")"
activities = UserActivity.where("(container_type = 'Project' and container_id in #{user_project_ids} and act_type in #{project_types})" +
"or (container_type = 'Course' and container_id in #{user_course_ids} and act_type in #{course_types}) "+
"or (container_type = 'Principal' and act_type= '#{principal_types}' and container_id = #{user.id}) " +
"or (container_type = 'Blog' and act_type= 'BlogComment' and container_id in #{blog_ids})").order('updated_at desc')
all_count = activities.count
activities = activities.limit(10).offset(page * 10)
count = activities.count
present :data, activities, with: Mobile::Entities::Activity,user: user
present :all_count, all_count
present :count, count
present :page, page
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,19 @@
#coding=utf-8
module Mobile
module Apis
class BlogComments< Grape::API
resources :blog_comments do
desc "get special topic"
get ':id' do
user = UserWechat.find_by_openid(params[:openid]).user
blog = BlogComment.find params[:id]
present :data, blog, with: Mobile::Entities::BlogComment,user: user
present :status, 0
end
end
end
end
end

View File

@ -115,7 +115,7 @@ module Mobile
desc '通知评论列表'
params do
requires :token, type: String
#requires :token, type: String
requires :notice_id,type:Integer,desc:'通知id'
optional :page,type:Integer,desc:'页码'
end

View File

@ -0,0 +1,19 @@
#coding=utf-8
module Mobile
module Apis
class Issues< Grape::API
resources :issues do
include IssuesHelper
desc "get special issuse"
get ':id' do
user = UserWechat.find_by_openid(params[:openid]).user
issue = Issue.find params[:id]
present :data, issue, with: Mobile::Entities::Issue,user: user
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,18 @@
#coding=utf-8
module Mobile
module Apis
class JournalForMessages< Grape::API
resources :journal_for_messages do
desc "get special journal"
get ':id' do
user = UserWechat.find_by_openid(params[:openid]).user
jour = JournalsForMessage.find params[:id]
present :data, jour, with: Mobile::Entities::Jours,user: user
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,18 @@
#coding=utf-8
module Mobile
module Apis
class Messages< Grape::API
resources :messages do
desc "get special topic"
get ':id' do
user = UserWechat.find_by_openid(params[:openid]).user
message = Message.find params[:id]
present :data, message, with: Mobile::Entities::Message,user: user
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,105 @@
#coding=utf-8
module Mobile
module Apis
class NewComment< Grape::API
include ApplicationHelper
include ApiHelper
resources :new_comment do
desc "add a new comment"
params do
requires :type, type: String
requires :content, type: String
requires :openid, type: String
end
post ':id' do
type = params[:type]
result = 1
current_user = UserWechat.find_by_openid(params[:openid]).user
if params[:content]!="" && current_user
case type
when "HomeworkCommon"
homework_common = HomeworkCommon.find(params[:id])
feedback = HomeworkCommon.add_homework_jour(current_user, params[:content], params[:id])
if (feedback.errors.empty?)
homework_common.update_column(:updated_at, Time.now)
result = 2
end
when "News"
news = News.find(params[:id])
comment = Comment.new
comment.comments = params[:content]
comment.author = current_user
if news.comments << comment
result = 2
end
when "Message"
message = Message.find(params[:id])
board = Board.find(message.board_id)
topic = message.root
reply = Message.new
reply.author = current_user
reply.board = board
reply.content = params[:content]
reply.parent_id = params[:id]
reply.subject = "RE: #{topic.subject}"
if topic.children << reply
result = 2
end
when "JournalsForMessage"
jour = JournalsForMessage.find params[:id]
parent_id = params[:id]
author_id = current_user.id
reply_user_id = jour.user_id
reply_id = params[:id]
content = params[:content]
options = {:user_id => author_id,
:status => true,
:m_parent_id => parent_id,
:m_reply_id => reply_id,
:reply_id => reply_user_id,
:notes => content,
:is_readed => false}
jfm = jour.user.add_jour(nil, nil, nil, options)
if jfm.errors.empty?
(JournalsForMessage.find parent_id).update_attribute(:updated_on,Time.now)
result = 2
end
when 'Issue'
issue = Issue.find params[:id]
is_jour = Journal.new
is_jour.user_id = current_user.id
is_jour.notes = params[:content]
is_jour.journalized = issue
if is_jour.save
result = 2
end
when 'BlogComment'
blog = BlogComment.find(params[:id]).root
blogComment = BlogComment.new
blogComment.author = current_user
blogComment.blog = blog.blog
blogComment.content = params[:content]
blogComment.title = "RE: #{blog.title}"
if blog.children << blogComment
result = 2
end
end
if result == 2
update_course_activity_api(type,params[:id])
update_user_activity_api(type,params[:id])
update_org_activity_api(type,params[:id])
update_forge_activity_api(type,params[:id])
update_principal_activity_api(type,params[:id])
end
else
result = 3
end
present :result, result
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,18 @@
#coding=utf-8
module Mobile
module Apis
class Newss< Grape::API
resources :newss do
desc "get special news"
get ':id' do
user = UserWechat.find_by_openid(params[:openid]).user
news = News.find params[:id]
present :data, news, with: Mobile::Entities::News,user: user
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,41 @@
#coding=utf-8
module Mobile
module Apis
class Praise< Grape::API
include ApiHelper
resources :praise do
desc "praise an activity"
params do
requires :type, type: String
requires :openid, type: String
end
post ':id' do
obj_id = params[:id]
obj_type = params[:type]
user = UserWechat.find_by_openid(params[:openid]).user
pts = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",obj_id,obj_type.to_s,user.id).first
if pts.blank?
praise_or_cancel(obj_type,obj_id,user,1)
obj = PraiseTreadCache.where("object_id=? and object_type=?",obj_id,obj_type.to_s).first
num = get_activity_praise_num(obj) if !obj.blank?
else
pts.destroy if !pts.blank?
#再更新praise_tread_cache表 使相应的记录减1 当为0时删除
ptc = PraiseTreadCache.where("object_id=? and object_type=?",obj_id,obj_type.to_s).first
ptc.praise_minus(1) if !ptc.blank?
if ptc.praise_num == 0
ptc.delete
end
obj = PraiseTreadCache.where("object_id=? and object_type=?",obj_id,obj_type.to_s).first
num = !obj.blank? ? get_activity_praise_num(obj) : 0
end
present :data, num
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,18 @@
#coding=utf-8
module Mobile
module Apis
class Whomeworks< Grape::API
resources :whomeworks do
desc "get one homework"
get ':id' do
user = UserWechat.find_by_openid(params[:openid]).user
homework = HomeworkCommon.find params[:id]
present :data, homework, with: Mobile::Entities::Whomework,user: user
present :status, 0
end
end
end
end
end

View File

@ -0,0 +1,146 @@
# encoding: utf-8
module Mobile
module Entities
class Activity <Grape::Entity
include ApplicationHelper
include ApiHelper
def self.act_expose(f)
expose f do |ac,opt|
if ac.is_a?(Hash) && ac.key?(f)
ac[f]
elsif ac.is_a?(::UserActivity)
if ac.respond_to?(f)
ac.send(f)
else
case f
when :user_act
if ac.act_type == "ProjectCreateInfo"
ac unless ac.nil?
else
ac.act unless ac.nil? || ac.act.nil?
end
when :reply_count
if ac.act_type == "HomeworkCommon"
ac.nil? || ac.act.nil? ? 0 : ac.act.journals_for_messages.count
elsif ac.act_type == "News"
ac.nil? || ac.act.nil? ? 0 : ac.act.comments.count
elsif ac.act_type == "Message" || ac.act_type == "BlogComment" || ac.act_type == "JournalsForMessage"
ac.nil? || ac.act.nil? ? 0 : ac.act.children.count
elsif ac.act_type == "Issue"
ac.nil? || ac.act.nil? ? 0 : ac.act.journals.where("notes is not null and notes != ''").count
end
when :subject
if ac.act_type == "HomeworkCommon"
ac.act.name unless ac.nil? || ac.act.nil?
elsif ac.act_type == "News" || ac.act_type == "BlogComment"
ac.act.title unless ac.nil? || ac.act.nil?
elsif ac.act_type == "Message" || ac.act_type == "Issue"
ac.act.subject unless ac.nil? || ac.act.nil?
elsif ac.act_type == "JournalsForMessage"
ac.act.private == 0 ? "留言" : "私信" unless ac.nil? || ac.act.nil?
end
when :description
if ac.act_type == "HomeworkCommon" || ac.act_type == "Issue" || ac.act_type == "News"
ac.act.description unless ac.nil? || ac.act.nil?
elsif ac.act_type == "Message" || ac.act_type == "BlogComment"
ac.act.content unless ac.nil? || ac.act.nil?
elsif ac.act_type == "JournalsForMessage"
ac.act.notes unless ac.nil? || ac.act.nil?
end
when :latest_update
time_from_now ac.updated_at unless ac.nil?
when :praise_count
if ac.act_type == "HomeworkCommon" || ac.act_type == "News" || ac.act_type == "Message" || ac.act_type == "BlogComment" || ac.act_type == "JournalsForMessage" || ac.act_type == "Issue"
ac.nil? || ac.act.nil? ? 0 : get_activity_praise_num(ac.act)
end
#when :homework_common_detail_manual
# if ac.act_type == "HomeworkCommon"
# ac.act.homework_detail_manual unless ac.nil? || ac.act.nil? || ac.act.homework_detail_manual.nil?
# end
when :course_project_name
if ac.container_type == "Course"
name = (get_course(ac.container_id)).name
name
elsif ac.container_type == "Project"
name = (get_project(ac.container_id)).name
name
elsif ac.container_type == "Blog"
"发表博客"
end
when :activity_type_name
if ac.container_type == "Course"
case ac.act_type
when "HomeworkCommon"
"课程作业"
when "News"
"课程通知"
when "Message"
"课程问答区"
when "Poll"
"课程问卷"
when "Course"
"课程"
end
elsif ac.container_type == "Project"
case ac.act_type
when "Issue"
"项目缺陷"
when "Message"
"项目讨论区"
when "ProjectCreateInfo"
"项目"
end
end
end
end
end
end
end
expose :act_type #缺陷/作业/讨论区/留言等类型
expose :act_id
expose :container_type #课程/项目/博客/个人
expose :author, using: Mobile::Entities::User do |a, opt| #用户信息
if a.is_a? ::UserActivity
if a.act_type == "ProjectCreateInfo"
get_user(get_project(a.act_id).user_id)
elsif !a.act.nil?
if a.act_type == 'Issue' || a.act_type == 'News' || a.act_type == 'Message' || a.act_type == 'BlogComment'
a.act.author
elsif a.act_type == 'HomeworkCommon' || a.act_type == 'Poll' || a.act_type == 'JournalsForMessage'
a.act.user
elsif a.act_type == 'Course'
a.act.teacher
end
end
end
end
expose :homework_common_detail , using: Mobile::Entities::Whomework do |a, opt| #作业相关信息
if a.act_type == "HomeworkCommon"
a.act
end
end
expose :issue_detail, using: Mobile::Entities::Issue do |a, opt| #缺陷信息
if a.act_type == "Issue"
a.act
end
end
act_expose :reply_count #回复数
act_expose :praise_count #点赞数
#act_expose :user_act #某个动态
act_expose :subject #标题
act_expose :description #描述
act_expose :latest_update #最新更新时间
act_expose :course_project_name #课程/项目名字
act_expose :activity_type_name #课程问答区/项目缺陷等
expose :has_praise , if: lambda { |instance, options| options[:user] } do |instance, options|
if instance.act_type == "HomeworkCommon" || instance.act_type == "News" || instance.act_type == "Message" || instance.act_type == "BlogComment" || instance.act_type == "JournalsForMessage" || instance.act_type == "Issue"
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.act_id,instance.act_type.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end
end

View File

@ -0,0 +1,65 @@
module Mobile
module Entities
class BlogComment < Grape::Entity
include ApplicationHelper
include ApiHelper
def self.blog_comment_expose(f)
expose f do |u,opt|
if u.is_a?(Hash) && u.key?(f)
u[f]
elsif u.is_a?(::BlogComment)
if u.respond_to?(f)
if f == :created_at
format_time( u.send(f))
else
u.send(f)
end
else
case f
when :praise_count
get_activity_praise_num(u)
when :lasted_comment
time_from_now(u.created_at)
when :act_type
'BlogComment'
when :act_id
u.id
when :comment_count
u.children.count
end
end
end
end
end
expose :user, using: Mobile::Entities::User do |c, opt|
if c.is_a?(::BlogComment)
c.author
end
end
blog_comment_expose :act_type
blog_comment_expose :act_id
blog_comment_expose :blog_id
blog_comment_expose :title
blog_comment_expose :content
blog_comment_expose :comment_count
blog_comment_expose :created_at
blog_comment_expose :lasted_comment
blog_comment_expose :id
blog_comment_expose :praise_count
expose :blog_comment_children, using:Mobile::Entities::BlogComment do |c,opt|
if c.is_a? (::BlogComment)
c.children.reverse
end
end
expose :has_praise, if: lambda { |instance, options| options[:user] } do |instance, options|
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.id,instance.class.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end

View File

@ -9,7 +9,7 @@ module Mobile
elsif f.is_a?(::Comment)
if f.respond_to?(field)
if field == :created_on
format_time(f.send(field))
time_from_now(f.send(field))
else
f.send(field)
end

View File

@ -0,0 +1,67 @@
module Mobile
module Entities
class Issue <Grape::Entity
include ApiHelper
include Redmine::I18n
def self.issue_expose(f)
expose f do |issue, opt|
if issue.is_a?(Hash) && issue.key?(f)
issue[f]
elsif issue.is_a?(::Issue)
if issue.respond_to?(f)
if f == :created_on
format_time(issue.send(f))
else
issue.send(f)
end
else
case f
when :issue_priority
get_issue_priority_api issue.priority_id
when :issue_assigned_to
(get_user(issue.assigned_to_id)).login
when :issue_status
IssueStatus.find(issue.status_id).name
when :journals_count
issue.journals.where("notes is not null and notes != ''").count
when :project_name
issue.project.name
when :praise_count
get_activity_praise_num(issue)
when :act_type
'Issue'
when :act_id
issue.id
end
end
end
end
end
expose :subject
expose :description
expose :author, using: Mobile::Entities::User
expose :done_ratio
issue_expose :act_type
issue_expose :act_id
issue_expose :created_on
issue_expose :issue_priority
issue_expose :issue_assigned_to
issue_expose :issue_status
issue_expose :journals_count
issue_expose :project_name
issue_expose :praise_count
expose :issue_journals, using: Mobile::Entities::Journal do |f, opt|
if f.is_a?(::Issue)
f.journals.where("notes is not null and notes != ''").reverse
end
end
expose :has_praise , if: lambda { |instance, options| options[:user] } do |instance, options|
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.id,instance.class.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end

View File

@ -0,0 +1,44 @@
module Mobile
module Entities
class Journal <Grape::Entity
include Redmine::I18n
=begin
include Redmine::I18n
include ApplicationHelper
include ApiHelper
include IssuesHelper
include ActionView::Helpers::TagHelper
include ActionView::Helpers::UrlHelper
=end
def self.journal_expose(f)
expose f do |journal, opt|
if journal.is_a?(Hash) && journal.key?(f)
journal[f]
elsif journal.is_a?(::Journal)
if journal.respond_to?(f)
if f == :created_on
time_from_now(journal.send(f))
else
journal.send(f)
end
else
case f
when :detail_journal
if journal.details.any?
details_to_strings(journal.details)
end
end
end
end
end
end
expose :notes
journal_expose :created_on
expose :user,using: Mobile::Entities::User do |f, opt|
f.user
end
#journal_expose :detail_journal
end
end
end

View File

@ -3,7 +3,6 @@ module Mobile
#普通留言
class Jours < Grape::Entity
include Redmine::I18n
include WordsHelper
def self.jours_expose(field)
expose field do |f,opt|
if f.is_a?(Hash) && f.key?(field)
@ -15,13 +14,24 @@ module Mobile
f.send(field)
end
else
case f
when :course_name
f[:jour_type] == "Course" ? f.course.name : ""
case field
when :lasted_comment
time_from_now f.created_on
when :reply_count
f.children.count
when :praise_count
get_activity_praise_num(f)
when :act_type
'JournalsForMessage'
when :act_id
f.id
end
end
end
end
jours_expose :act_type
jours_expose :act_id
jours_expose :id
jours_expose :jour_type
jours_expose :jour_id
@ -29,9 +39,12 @@ module Mobile
f.user
end
jours_expose :created_on
jours_expose :lasted_comment
jours_expose :notes
jours_expose :m_reply_id
jours_expose :m_parent_id
jours_expose :reply_count
jours_expose :praise_count
expose :course,using:Mobile::Entities::Course do |f,opt|
if f.is_a?(::JournalsForMessage) && f[:jour_type] == "Course"
f.course
@ -42,9 +55,16 @@ module Mobile
end
expose :child_reply,using: Mobile::Entities::Jours do |f, opt|
if f.is_a?(::JournalsForMessage)
fetch_user_leaveWord_reply(f)
f.children.reverse
end
end
expose :has_praise , if: lambda { |instance, options| options[:user] } do |instance, options|
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.id,instance.class.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end

View File

@ -15,10 +15,22 @@ module Mobile
u.send(f)
end
else
# case f
# when :xx
# #
# end
case f
when :course_project_name
if u.board.project_id == -1
u.course.name
else
u.project.name
end
when :lasted_comment
time_from_now u.created_on
when :praise_count
get_activity_praise_num(u)
when :act_type
'Message'
when :act_id
u.id
end
end
end
@ -30,17 +42,29 @@ module Mobile
c.author
end
end
message_expose :act_type
message_expose :act_id
message_expose :course_project_name
message_expose :board_id
message_expose :subject
message_expose :content
message_expose :replies_count
message_expose :praise_count
message_expose :created_on
message_expose :id
message_expose :lasted_comment
expose :message_children,using:Mobile::Entities::Message do |c,opt|
if c.is_a? (::Message)
c.children
c.children.reverse
end
end
expose :has_praise , if: lambda { |instance, options| options[:user] } do |instance, options|
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.id,instance.class.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end

View File

@ -2,15 +2,29 @@ module Mobile
module Entities
class News < Grape::Entity
include Redmine::I18n
include ApiHelper
def self.news_expose(field)
expose field do |f,opt|
if f.is_a?(Hash) && f.key?(field)
f[field]
elsif f.is_a?(::News)
if field == :created_on
format_time(f.send(field)) if f.respond_to?(field)
if f.respond_to?(field)
if field == :created_on
format_time(f.send(field))
else
f.send(field)
end
else
f.send(field) if f.respond_to?(field)
case field
when :course_name
get_course(f.course_id).name unless f.course_id == nil
when :praise_count
get_activity_praise_num(f)
when :act_type
'News'
when :act_id
f.id
end
end
elsif f.is_a?(Hash) && !f.key?(field)
n = f[:news]
@ -40,6 +54,8 @@ module Mobile
end
obj
end
news_expose :act_type
news_expose :act_id
#作者id
news_expose :author_id
#作者名
@ -52,16 +68,24 @@ module Mobile
news_expose :created_on
#评论数量
news_expose :comments_count
news_expose :praise_count
#课程名字
news_expose :course_name
#评论
expose :comments, using: Mobile::Entities::Comment do |f, opt|
if f.is_a?(Hash) && f.key?(:comments)
f[:comments]
elsif f.is_a?(::News) && f.respond_to?(:comments)
f.send(:comments)
f.comments.reverse
end
end
expose :has_praise , if: lambda { |instance, options| options[:user] } do |instance, options|
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.id,instance.class.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end

View File

@ -32,14 +32,14 @@ module Mobile
end
end
student_work_expose :student_id
student_work_expose :id
student_work_expose :id
student_work_expose :name
student_work_expose :description
student_work_expose :final_score
student_work_expose :teacher_score
student_work_expose :student_score
student_work_expose :teacher_asistant_score
student_work_expose :created_at
student_work_expose :description
student_work_expose :final_score
student_work_expose :teacher_score
student_work_expose :student_score
student_work_expose :teacher_asistant_score
student_work_expose :created_at
end
end
end

View File

@ -0,0 +1,85 @@
# encoding: utf-8
module Mobile
module Entities
class Whomework <Grape::Entity
include ApiHelper
include ApplicationHelper
include Redmine::I18n
def self.whomework_expose(f)
expose f do |wh, opt|
if wh.is_a?(Hash) && wh.key?(f)
wh[f]
elsif wh.is_a?(::HomeworkCommon)
if wh.respond_to?(f)
if f == :created_at
format_time(wh.send(f))
else
wh.send(f)
end
else
case f
when :absence_penalty
wh.nil? || wh.homework_detail_manual.nil? ? 0 : wh.homework_detail_manual.absence_penalty
when :evaluation_start
wh.nil? || wh.homework_detail_manual.nil? ? nil : convert_to_time(wh.homework_detail_manual.evaluation_start, 0)
when :evaluation_end
wh.nil? || wh.homework_detail_manual.nil? ? nil : convert_to_time(wh.homework_detail_manual.evaluation_end, 1)
when :praise_count
get_activity_praise_num(wh)
when :whomework_journal_count
wh.journals_for_messages.count
when :course_name
wh.course.name
when :act_type
'HomeworkCommon'
when :act_id
wh.id
end
end
end
end
end
expose :author, using: Mobile::Entities::User do |w, opt|
if w.is_a?(::HomeworkCommon)
w.user
end
end
expose :current_user, using: Mobile::Entities::User do |w, opt|
current_user
end
expose :name
expose :description
expose :publish_time
expose :end_time
expose :homework_type
expose :late_penalty
expose :course_id
expose :anonymous_comment
expose :quotes
expose :is_open
whomework_expose :act_type
whomework_expose :act_id
whomework_expose :course_name
whomework_expose :created_at
whomework_expose :absence_penalty
whomework_expose :evaluation_start
whomework_expose :evaluation_end
whomework_expose :praise_count
whomework_expose :whomework_journal_count
expose :journals_for_messages, using: Mobile::Entities::Jours do |f, opt|
#f[:journals_for_messages] if f.is_a?(Hash) && f.key?(:journals_for_messages)
if f.is_a?(::HomeworkCommon)
f.journals_for_messages.reverse
end
end
expose :has_praise , if: lambda { |instance, options| options[:user] } do |instance, options|
has_praise = false
current_user = options[:user]
obj = PraiseTread.where("praise_tread_object_id=? and praise_tread_object_type=? and user_id=?",instance.id,instance.class.to_s,current_user.id)
has_praise = obj.empty? ? false : true
has_praise
end
end
end
end

View File

@ -7,7 +7,8 @@ module Mobile
@app.call(@env)
rescue =>e
message = {status: 1, message: e.message }.to_json
puts(e.backtrace.join("\n")) if Rails.env.development?
Rails.logger.error e.inspect
Rails.logger.error e.backtrace.join("\n")
status = 200
headers = { 'Content-Type' => content_type }
Rack::Response.new([message], status, headers).finish

View File

@ -660,8 +660,10 @@ class AttachmentsController < ApplicationController
end
def has_login
unless @attachment && @attachment.container_type == "PhoneAppVersion"
render_403 if !User.current.logged? && !(@attachment.container_type == 'OrgSubfield' && @attachment.container.organization.allow_guest_download) && !(@attachment.container_type == 'OrgDocumentComment' && @attachment.container.organization.allow_guest_download)
unless @attachment && @attachment.container_type == "Organization"
unless @attachment && @attachment.container_type == "PhoneAppVersion"
render_403 if !User.current.logged? && !(@attachment.container_type == 'OrgSubfield' && @attachment.container.organization.allow_guest_download) && !(@attachment.container_type == 'OrgDocumentComment' && @attachment.container.organization.allow_guest_download)
end
end
end
end

View File

@ -79,8 +79,8 @@ class OrganizationsController < ApplicationController
if @organization.show_mode == 1 && params[:org_subfield_id].nil? && params[:list] .nil?
if @organization.is_public? || User.current.admin? || User.current.member_of_org?(@organization)
# REDO:时间紧,暂时先这样
@org_logo_attchment = Attachment.where("container_id =? and container_type =? and attachtype =?", @organization, "Organization", 0).order("created_on desc").first
@org_banner_attchment = Attachment.where("container_id =? and container_type =? and attachtype =?", @organization, "Organization", 1).order("created_on desc").first
@org_logo_attchment = Attachment.find_by_sql("SELECT * from attachments WHERE container_id = #{@organization.id} and container_type = 'Organization' and attachtype = 0 and filename REGEXP '(.jpg|.png|.bmp|.gif|.jpeg)' ORDER BY created_on desc limit 1").first
@org_banner_attchment = Attachment.find_by_sql("SELECT * from attachments WHERE container_id = #{@organization.id} and container_type = 'Organization' and attachtype = 1 and filename REGEXP '(.jpg|.png|.bmp|.gif|.jpeg)' ORDER BY created_on desc limit 1").first
@subfield_content = @organization.org_subfields.order("priority")
@organization = Organization.find(params[:id])
@ -352,7 +352,7 @@ class OrganizationsController < ApplicationController
if !params[:name].nil?
condition = "%#{params[:name].strip}%".gsub(" ","")
end
sql = "select courses.* from courses inner join members on courses.id = members.course_id where members.user_id = #{User.current.id} and courses.name like '#{condition}'"+
sql = "select courses.* from courses inner join members on courses.id = members.course_id where members.user_id = #{User.current.id} and courses.is_public = 1 and courses.name like '#{condition}'"+
"and courses.id not in (select distinct org_courses.course_id from org_courses where org_courses.organization_id = #{@organization.id}) and courses.is_delete=0"
#user_courses = Course.find_by_sql(sql)
@courses = Course.find_by_sql(sql)
@ -396,7 +396,7 @@ class OrganizationsController < ApplicationController
if !params[:name].nil?
condition = "%#{params[:name].strip}%".gsub(" ","")
end
sql = "select projects.* from projects inner join members on projects.id = members.project_id where members.user_id = #{User.current.id} and projects.status != 9 and projects.name like '#{condition}'" +
sql = "select projects.* from projects inner join members on projects.id = members.project_id where members.user_id = #{User.current.id} and projects.status != 9 and projects.is_public = 1 and projects.name like '#{condition}'" +
" and projects.id not in (select org_projects.project_id from org_projects where organization_id = #{@organization.id}) and status=1"
#user_projects = Course.find_by_sql(sql)
@projects = Course.find_by_sql(sql)

View File

@ -450,7 +450,7 @@ class StudentWorkController < ApplicationController
stu_project.save
end
end
@homework.update_attributes(:updated_at => Time.now)
@homework.update_column(:updated_at, Time.now)
update_course_activity(@homework.class,@homework.id)
update_user_activity(@homework.class,@homework.id)
update_org_activity(@homework.class,@homework.id)

View File

@ -836,8 +836,8 @@ class UsersController < ApplicationController
render_403
return
end
user_course_ids = User.current.courses.map { |c| c.id}
user_project_ids = User.current.projects.map {|p| p.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
user_project_ids = User.current.projects.map {|p| p.status != 9 && p.id }
# user_org_ids = User.current.organizations.map {|o| o.id}
if(params[:type].blank? || params[:type] == "1") # 我的资源
# 修正:我的资源库的话,那么应该是我上传的所有资源加上,我加入的课程、项目、组织的所有资源
@ -886,11 +886,11 @@ class UsersController < ApplicationController
def user_ref_resource_search
search = params[:search].to_s.strip.downcase
if(params[:type].blank? || params[:type] == "1") #全部
user_course_ids = User.current.courses.map { |c| c.id} #我的资源库的话,那么应该是我上传的所有资源 加上 我加入的课程的所有资源 取交集并查询
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id} #我的资源库的话,那么应该是我上传的所有资源 加上 我加入的课程的所有资源 取交集并查询
@attachments = Attachment.where("((author_id = #{params[:id]} and container_type in('Project','Principal','Course','Issue','Document','Message','News','StudentWorkScore','HomewCommon')) "+
" or (container_type = 'Course' and container_id in (#{user_course_ids.empty? ? '0': user_course_ids.join(',')}))) and (filename like '%#{search}%') ").order("created_on desc")
elsif params[:type] == "2" #课程资源
user_course_ids = User.current.courses.map { |c| c.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
@attachments = Attachment.where("(author_id = #{params[:id]} and container_type = 'Course') or (container_type = 'Course' and container_id in (#{user_course_ids.empty? ? '0': user_course_ids.join(',')})) and (filename like '%#{search}%') ").order("created_on desc")
elsif params[:type] == "3" #项目资源
@attachments = Attachment.where("author_id = #{params[:id]} and container_type = 'Project' and (filename like '%#{search}%')").order("created_on desc")
@ -1579,16 +1579,14 @@ class UsersController < ApplicationController
# 上传用户资源
def user_resource_create
user_course_ids = User.current.courses.map { |c| c.id}
user_project_ids = User.current.projects.map {|p| p.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
user_project_ids = User.current.projects.map {|p| p.status != 9 && p.id }
# user_org_ids = User.current.organizations.map {|o| o.id}
@user = User.find(params[:id])
# 保存文件
attach = Attachment.attach_filesex_public(@user, params[:attachments], params[:attachment_type], is_public = true)
@order, @b_sort = params[:order] || "created_on", params[:sort] || "asc"
@score = @b_sort == "desc" ? "asc" : "desc"
user_course_ids = User.current.courses.map { |c| c.id}
user_project_ids = User.current.projects.map {|p| p.id}
# user_org_ids = User.current.organizations.map {|o| o.id}
if(params[:type].blank? || params[:type] == "1") # 我的资源
# 修正:我的资源库的话,那么应该是我上传的所有资源加上,我加入的课程、项目、组织的所有资源
@ -2467,7 +2465,7 @@ class UsersController < ApplicationController
# 获取我的资源
def get_my_resources author_id, user_course_ids, user_project_ids, order, score
attachments = Attachment.where("(author_id = #{author_id} and is_publish = 1 and container_id is not null and container_type in('Project','OrgSubfield','Principal','Course','Issue','Document','Message','News','StudentWorkScore','HomewCommon')) "+
attachments = Attachment.where("(author_id = #{author_id} and is_publish = 1 and container_id is not null and container_type in('OrgSubfield','Principal','Issue','Document','Message','News','StudentWorkScore','HomewCommon')) "+
"or (container_type = 'Course' and container_id in (#{user_course_ids.empty? ? '0': user_course_ids.join(',')}) and is_publish = 1 and container_id is not null)" +
"or (container_type = 'Project' and container_id in (#{user_project_ids.empty? ? '0': user_project_ids.join(',')}) and is_publish = 1 and container_id is not null)" ).order("#{order.nil? ? 'created_on' : order} #{score}")
end
@ -2580,8 +2578,8 @@ class UsersController < ApplicationController
end
@order, @b_sort = params[:order] || "created_on", params[:sort] || "asc"
@score = @b_sort == "desc" ? "asc" : "desc"
user_course_ids = User.current.courses.map { |c| c.id}
user_project_ids = User.current.projects.map {|p| p.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
user_project_ids = User.current.projects.map {|p| p.status != 9 && p.id }
# user_org_ids = User.current.organizations.map {|o| o.id}
if(params[:type].blank? || params[:type] == "1") # 我的资源
# 修正:我的资源库的话,那么应该是我上传的所有资源加上,我加入的课程、项目、组织的所有资源
@ -2638,8 +2636,8 @@ class UsersController < ApplicationController
@user = User.find(params[:id])
@order, @b_sort = params[:order] || "created_on", params[:sort] || "asc"
@score = @b_sort == "desc" ? "asc" : "desc"
user_course_ids = User.current.courses.map { |c| c.id}
user_project_ids = User.current.projects.map {|p| p.id} # user_org_ids = User.current.organizations.map {|o| o.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
user_project_ids = User.current.projects.map {|p| p.status != 9 && p.id } # user_org_ids = User.current.organizations.map {|o| o.id}
if(params[:type].blank? || params[:type] == "1") # 我的资源
# 修正:我的资源库的话,那么应该是我上传的所有资源加上,我加入的课程、项目、组织的所有资源
@attachments = get_my_resources(params[:id], user_course_ids, user_project_ids, @order, @score)
@ -2677,8 +2675,8 @@ class UsersController < ApplicationController
# 别人的资源库是没有权限去看的
if(params[:type].blank? || params[:type] == "1") # 我的资源
# 修正:我的资源库的话,那么应该是我上传的所有资源加上,我加入的课程、项目、组织的所有资源
user_course_ids = User.current.courses.map { |c| c.id}
user_project_ids = User.current.projects.map {|p| p.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
user_project_ids = User.current.projects.map {|p| p.status != 9 && p.id }
# user_org_ids = User.current.organizations.map {|o| o.id}
@attachments = get_my_resources_search(params[:id], user_course_ids, user_project_ids, @order, @score, search)
elsif params[:type] == "6" # 公共资源
@ -2735,6 +2733,10 @@ class UsersController < ApplicationController
attach_copied_obj.attachtype = 4
end
attach_copied_obj.save
# 附件保存成功后更新项目和课程的统计数
if params[:mul_type] == "Project"
mul_container.project_score.update_attribute(:attach_num, mul_container.project_score.attach_num + 1) unless mul_container.project_score.nil?
end
@save_message = attach_copied_obj.errors.full_messages
end
end
@ -2758,8 +2760,8 @@ class UsersController < ApplicationController
@user = User.current
@switch_search = params[:search].nil? ? " " : params[:search]
search = "%#{@switch_search.strip.downcase}%"
user_course_ids = User.current.courses.map {|c| c.id}
user_project_ids = User.current.projects.map {|p| p.id}
user_course_ids = User.current.courses.map { |c| c.is_delete == 0 && c.id}
user_project_ids = User.current.projects.map {|p| p.status != 9 && p.id }
if(params[:type].nil? || params[:type].blank? || params[:type] == "1" || params[:type] == 'all') # 全部
if params[:status] == "2"
@attachments = get_course_resources_search(params[:id], user_course_ids, @order, @score, search)

View File

@ -0,0 +1,235 @@
#coding=utf-8
class WechatsController < ActionController::Base
wechat_responder
include ApplicationHelper
# default text responder when no other match
on :text do |request, content|
request.reply.text "您的意见已收到" # Just echo
end
# When receive 'help', will trigger this responder
on :text, with: 'help' do |request|
request.reply.text 'help content'
end
# When receive '<n>news', will match and will got count as <n> as parameter
on :text, with: /^(\d+) news$/ do |request, count|
# Wechat article can only contain max 10 items, large than 10 will dropped.
news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: 'News title', content: "No. #{n} news content" } }
request.reply.news(news) do |article, n, index| # article is return object
article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/'
end
end
on :event, with: 'subscribe' do |request|
default_msg(request)
end
# When unsubscribe user scan qrcode qrscene_xxxxxx to subscribe in public account
# notice user will subscribe public account at same time, so wechat won't trigger subscribe event any more
on :scan, with: 'qrscene_xxxxxx' do |request, ticket|
request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}"
end
# When subscribe user scan scene_id in public account
on :scan, with: 'scene_id' do |request, ticket|
request.reply.text "Subscribe user #{request[:FromUserName]} Ticket #{ticket}"
end
# When no any on :scan responder can match subscribe user scaned scene_id
on :event, with: 'scan' do |request|
if request[:EventKey].present?
request.reply.text "event scan got EventKey #{request[:EventKey]} Ticket #{request[:Ticket]}"
end
end
# When enterprise user press menu BINDING_QR_CODE and success to scan bar code
on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type|
request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}"
end
# Except QR code, wechat can also scan CODE_39 bar code in enterprise account
on :scan, with: 'BINDING_BARCODE' do |message, scan_result|
if scan_result.start_with? 'CODE_39,'
message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}"
end
end
# When user click the menu button
on :click, with: 'BOOK_LUNCH' do |request, key|
request.reply.text "User: #{request[:FromUserName]} click #{key}"
end
# When user view URL in the menu button
on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view|
request.reply.text "#{request[:FromUserName]} view #{view}"
end
# When user sent the imsage
on :image do |request|
request.reply.image(request[:MediaId]) # Echo the sent image to user
end
# When user sent the voice
on :voice do |request|
request.reply.voice(request[:MediaId]) # Echo the sent voice to user
end
# When user sent the video
on :video do |request|
nickname = wechat.user(request[:FromUserName])['nickname'] # Call wechat api to get sender nickname
request.reply.video(request[:MediaId], title: 'Echo', description: "Got #{nickname} sent video") # Echo the sent video to user
end
# When user sent location
on :location do |request|
request.reply.text("Latitude: #{message[:Latitude]} Longitude: #{message[:Longitude]} Precision: #{message[:Precision]}")
end
on :event, with: 'unsubscribe' do |request|
request.reply.success # user can not receive this message
end
# When user enter the app / agent app
on :event, with: 'enter_agent' do |request|
request.reply.text "#{request[:FromUserName]} enter agent app now"
end
# When batch job create/update user (incremental) finished.
on :batch_job, with: 'sync_user' do |request, batch_job|
request.reply.text "sync_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
end
# When batch job replace user (full sync) finished.
on :batch_job, with: 'replace_user' do |request, batch_job|
request.reply.text "replace_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
end
# When batch job invent user finished.
on :batch_job, with: 'invite_user' do |request, batch_job|
request.reply.text "invite_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
end
# When batch job replace department (full sync) finished.
on :batch_job, with: 'replace_party' do |request, batch_job|
request.reply.text "replace_party job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}"
end
# Any not match above will fail to below
on :fallback, respond: 'fallback message'
on :click, with: 'FEEDBACK' do |request, key|
request.reply.text "如有反馈问题,请直接切入至输入框,发微信给我们即可"
end
on :click, with: 'MY_NEWS' do |request, key|
default_msg(request)
end
def default_msg(request)
uw = user_binded?(request[:FromUserName])
if uw && uw.user
request.reply.text "欢迎回来, #{uw.user.show_name}"
else
sendBind(request)
end
end
def sendBind(request)
news = (1..1).each_with_object([]) { |n, memo| memo << { title: '绑定登录', content: "您还未绑定确实的用户,请先绑定." } }
request.reply.news(news) do |article, n, index| # article is return object
url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{login_wechat_url}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect"
article.item title: "#{n[:title]}",
description: n[:content],
pic_url: 'http://wechat.trustie.net/images/trustie_logo2.png',
url: url
end
end
def get_open_id
begin
raise "非法操作, code不存在" unless params[:code]
openid = get_openid_from_code(params[:code])
raise "无法获取到openid" unless openid
render :json => {status:0, openid: openid}
rescue Exception=>e
render :json => {status: -1, msg: e.message}
end
end
def bind
begin
raise "非法操作, code不存在" unless params[:code]
openid = get_openid_from_code(params[:code])
raise "无法获取到openid" unless openid
raise "此微信号已绑定用户, 不能得复绑定" if user_binded?(openid)
user, last_login_on = User.try_to_login(params[:username], params[:password])
raise "用户名或密码错误,请重新登录" unless user
#补全用户信息
raise "此用户已经绑定了公众号" if user.user_wechat
UserWechat.create!(
openid: openid,
user: user
)
render :json => {status:0, msg: "绑定成功"}
rescue Exception=>e
render :json => {status: -1, msg: e.message}
end
end
def login
@code = params[:code] #TODO 安全性
render 'wechats/login', layout: 'base_wechat'
end
private
def get_openid_from_code(code)
url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=#{Wechat.config.appid}&secret=#{Wechat.config.secret}&code=#{code}&grant_type=authorization_code"
logger.debug url
body = URI.parse(url).read
logger.debug body
JSON.parse(body)["openid"]
end
def user_binded?(openid)
uw = UserWechat.where(openid: openid).first
end
def user_activity(user)
@user = user
shield_project_ids = ShieldActivity.where("container_type='User' and container_id=#{@user.id} and shield_type='Project'").map(&:shield_id)
shield_course_ids = ShieldActivity.where("container_type='User' and container_id=#{@user.id} and shield_type='Course'").map(&:shield_id)
@page = params[:page] ? params[:page].to_i + 1 : 0
user_project_ids = (@user.projects.visible.map{|project| project.id}-shield_project_ids).empty? ? "(-1)" : "(" + (@user.projects.visible.map{|project| project.id}-shield_project_ids).join(",") + ")"
user_course_ids = (@user.courses.visible.map{|course| course.id}-shield_course_ids).empty? ? "(-1)" : "(" + (@user.courses.visible.map{|course| course.id}-shield_course_ids).join(",") + ")"
course_types = "('Message','News','HomeworkCommon','Poll','Course')"
project_types = "('Message','Issue','ProjectCreateInfo')"
principal_types = "JournalsForMessage"
blog_ids = "("+@user.blog.id.to_s+","+((User.watched_by(@user.id).count == 0 )? '0' :User.watched_by(@user.id).map{|u| u.blog.id}.join(','))+")"
@user_activities = UserActivity.where("(container_type = 'Project' and container_id in #{user_project_ids} and act_type in #{project_types})" +
"or (container_type = 'Course' and container_id in #{user_course_ids} and act_type in #{course_types}) "+
"or (container_type = 'Principal' and act_type= '#{principal_types}' and container_id = #{@user.id}) " +
"or (container_type = 'Blog' and act_type= 'BlogComment' and container_id in #{blog_ids})").order('updated_at desc').limit(10).offset(@page * 10)
end
def process_activity(user_activity)
act= user_activity.act
case user_activity.container_type.to_s
when 'Course'
when 'Project'
case user_activity.act_type.to_s
when 'Issue'
[act.project.name.to_s+" | 项目问题", act.subject.to_s, url_to_avatar(act.author),"http://wechat.trustie.net/app.html#/issue/#{act.id}"]
end
end
end
end

View File

@ -276,7 +276,7 @@ class WordsController < ApplicationController
ids = params[:asset_id].split(',')
update_kindeditor_assets_owner ids,feedback[:id],OwnerTypeHelper::JOURNALSFORMESSAGE
end
@homework_common.update_attributes(:updated_at => Time.now)
@homework_common.update_column(:updated_at, Time.now)
update_course_activity(@homework_common.class,@homework_common.id)
update_user_activity(@homework_common.class,@homework_common.id)
update_org_activity(@homework_common.class,@homework_common.id)

View File

@ -201,5 +201,288 @@ module ApiHelper
end
#日期转换为时间
def convert_to_time date, num
if num == 0
date = date.to_s + " 00:00"
elsif num == 1
date = date.to_s + " 23:59"
end
return date
end
#获取用户
def get_user user_id
user = User.find user_id
user
end
#获取项目
def get_project project_id
project = Project.find project_id
project
end
#获取课程
def get_course course_id
course = Course.find course_id
course
end
#获取点赞数
def get_activity_praise_num(object)
obj_type = object.class
obj_id = object.id
record = PraiseTreadCache.find_by_object_id_and_object_type(obj_id,obj_type)
if record
return ((record.praise_num.nil? ? 0 : record.praise_num.to_i)-(record.tread_num.nil? ? 0 : record.tread_num.to_i))
else
return 0
end
end
#获取缺陷的优先级
def get_issue_priority_api value
issuetype = ""
if value == 4
issuetype = "紧急"
elsif value == 2
issuetype = "正常"
elsif value == 3
issuetype = ""
elsif value == 1
issuetype = ""
else
issuetype = "立刻"
end
end
def jdetails_to_strings(details, no_html=false, options={})
options[:only_path] = (options[:only_path] == false ? false : true)
options[:token] = options[:token] if options[:token]
strings = []
values_by_field = {}
details.each do |detail|
if detail.property == 'cf'
field_id = detail.prop_key
field = CustomField.find_by_id(field_id)
if field && field.multiple?
values_by_field[field_id] ||= {:added => [], :deleted => []}
if detail.old_value
values_by_field[field_id][:deleted] << detail.old_value
end
if detail.value
values_by_field[field_id][:added] << detail.value
end
next
end
end
strings << jshow_detail(detail, no_html, options)
end
values_by_field.each do |field_id, changes|
detail = JournalDetail.new(:property => 'cf', :prop_key => field_id)
if changes[:added].any?
detail.value = changes[:added]
strings << jshow_detail(detail, no_html, options)
elsif changes[:deleted].any?
detail.old_value = changes[:deleted]
strings << jshow_detail(detail, no_html, options)
end
end
strings
end
# Returns the textual representation of a single journal detail
def jshow_detail(detail, no_html=false, options={})
multiple = false
case detail.property
when 'attr'
field = detail.prop_key.to_s.gsub(/\_id$/, "")
label = l(("field_" + field).to_sym)
case detail.prop_key
when 'due_date', 'start_date'
value = format_date(detail.value.to_date) if detail.value
old_value = format_date(detail.old_value.to_date) if detail.old_value
when 'project_id', 'status_id', 'tracker_id', 'assigned_to_id',
'priority_id', 'category_id', 'fixed_version_id'
value = find_name_by_reflection(field, detail.value)
old_value = find_name_by_reflection(field, detail.old_value)
when 'estimated_hours'
value = "%0.02f" % detail.value.to_f unless detail.value.blank?
old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
when 'parent_id'
label = l(:field_parent_issue)
value = "##{detail.value}" unless detail.value.blank?
old_value = "##{detail.old_value}" unless detail.old_value.blank?
when 'is_private'
value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
end
when 'cf'
custom_field = CustomField.find_by_id(detail.prop_key)
if custom_field
multiple = custom_field.multiple?
label = custom_field.name
value = format_value(detail.value, custom_field.field_format) if detail.value
old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
end
when 'attachment'
label = l(:label_attachment)
end
call_hook(:helper_issues_show_detail_after_setting,
{:detail => detail, :label => label, :value => value, :old_value => old_value })
label ||= detail.prop_key
value ||= detail.value
old_value ||= detail.old_value
unless no_html
label = content_tag('strong', label)
old_value = content_tag("i", old_value) if detail.old_value
old_value = content_tag("del", old_value) if detail.old_value and detail.value.blank?
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
if options[:token].nil?
value = atta.filename
else
value = atta.filename
end
# 放大镜搜索功能
# if options[:only_path] != false && atta.is_text?
# value += link_to(
# image_tag('magnifier.png'),
# :controller => 'attachments', :action => 'show',
# :id => atta, :filename => atta.filename
# )
# end
else
value = content_tag("i", value) if value
end
end
# 缺陷更新结果在消息中显示样式
if no_html == "message"
label = content_tag(:span, label, :class => "issue_update_message")
old_value = content_tag("span", old_value) if detail.old_value
old_value = content_tag("del", old_value) if detail.old_value and detail.value.blank?
if detail.property == 'attachment' && !value.blank? && atta = Attachment.find_by_id(detail.prop_key)
# Link to the attachment if it has not been removed
if options[:token].nil?
value = atta.filename
else
value = atta.filename
end
else
value = content_tag(:span, value, :class => "issue_update_message_value") if value
end
end
if detail.property == 'attr' && detail.prop_key == 'description'
s = l(:text_journal_changed_no_detail, :label => label)
unless no_html
diff_link = link_to l(:label_diff),
{:controller => 'journals', :action => 'diff', :id => detail.journal_id,
:detail_id => detail.id, :only_path => options[:only_path]},
:title => l(:label_view_diff)
s << " (#{ diff_link })"
end
s.html_safe
elsif detail.value.present?
case detail.property
when 'attr', 'cf'
if detail.old_value.present?
l(:text_journal_changed, :label => label, :old => old_value, :new => value).html_safe
elsif multiple
l(:text_journal_added, :label => label, :value => value).html_safe
else
l(:text_journal_set_to, :label => label, :value => value).html_safe
end
when 'attachment'
l(:text_journal_added, :label => label, :value => value).html_safe
end
else
l(:text_journal_deleted, :label => label, :old => old_value).html_safe
end
end
#课程动态的更新
def update_course_activity_api type, id
course_activity = CourseActivity.where("course_act_type=? and course_act_id =?", type.to_s, id.to_i).first
if course_activity
course_activity.updated_at = Time.now
course_activity.save
end
end
#首页动态更新
def update_user_activity_api type, id
user_activity = UserActivity.where("act_type=? and act_id =?", type.to_s, id.to_i).first
if user_activity
user_activity.updated_at = Time.now
user_activity.save
end
end
#项目动态更新
def update_forge_activity_api type, id
forge_activity = ForgeActivity.where("forge_act_type=? and forge_act_id=?", type.to_s, id.to_i).first
if forge_activity
forge_activity.updated_at = Time.now
forge_activity.save
end
end
#组织动态更新
def update_org_activity_api type , id
org_activity = OrgActivity.where("org_act_type=? and org_act_id =?", type.to_s, id.to_i).first
if org_activity
org_activity.updated_at = Time.now
org_activity.save
end
end
#个人动态更新
def update_principal_activity_api type, id
principal_activity = PrincipalActivity.where("principal_act_type=? and principal_act_id =?", type.to_s, id.to_i).first
if principal_activity
principal_activity.updated_at = Time.now
principal_activity.save
end
end
#赞/取消赞
def praise_or_cancel(type,id,user,flag)
unless id.nil? and type.nil?
#首先创建或更新praise_tread 表
pt = PraiseTread.new
pt.user_id = user.id
pt.praise_tread_object_id = id.to_i
pt.praise_tread_object_type = type
pt.praise_or_tread = flag
pt.save
# end
#再创建或更新praise_tread_cache表
#@ptc = PraiseTreadCache.find_by_object_id_and_object_type(id,type)
ptc = PraiseTreadCache.where("object_id = ? and object_type = ?",id.to_i,type).first
ptc = ptc.nil? ? PraiseTreadCache.new : ptc
ptc.object_id = id.to_i
ptc.object_type = type
ptc.save
ptc.praise_plus(flag,1)
end
end
def praise_plus(flag,num)
case flag
when 1
self.update_attribute(:praise_num, self.praise_num.to_i + num)
end
end
def praise_minus(num)
self.update_attribute(:praise_num, self.praise_num.to_i - num)
end
end

View File

@ -2104,6 +2104,8 @@ module ApplicationHelper
attachment.container.board.course
course = attachment.container.board.course
candown= User.current.member_of_course?(course) || (course.is_public==1 && attachment.is_public == 1)
elsif attachment.container.class.to_s=="Organization"
candown = true
elsif attachment.container.class.to_s=="HomeworkAttach"
candown = true
elsif attachment.container.class.to_s=="StudentWorksScore"

View File

@ -19,7 +19,7 @@
module IssuesHelper
include ApplicationHelper
include TagsHelper
def issue_list(issues, &block)
ancestors = []
issues.each do |issue|

View File

@ -1,5 +1,8 @@
#encoding: utf-8
class BlogComment < ActiveRecord::Base
# attr_accessible :title, :body
require 'net/http'
require 'json'
include Redmine::SafeAttributes
belongs_to :blog
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
@ -18,7 +21,7 @@ class BlogComment < ActiveRecord::Base
after_save :add_user_activity
after_update :update_activity
after_create :update_parent_time
after_create :update_parent_time, :blog_wechat_message
before_destroy :destroy_user_activity
scope :like, lambda {|arg|
@ -72,4 +75,51 @@ class BlogComment < ActiveRecord::Base
end
def project
end
#博客回复微信模板消息
def blog_wechat_message
# 发布博客
unless self.parent_id.nil?
uw = UserWechat.where(user_id: self.parent.author_id).first
#unless uw.nil? && self.parent.author_id != User.current.id
unless uw.nil?
data = {
touser:uw.openid,
template_id:"A_3f5v90-zK73V9Kijm-paDkl9S-NuM8Cf-1UJi92_c",
url:"http://www.trustie.net/",
topcolor:"#FF0000",
data:{
first: {
value:"您的博客有新回复了",
color:"#173177"
},
keyword1:{
value:self.author.try(:realname),
color:"#173177"
},
keyword2:{
value:format_time(self.created_at),
color:"#173177"
},
keyword3:{
value:self.content.html_safe,
color:"#173177"
},
remark:{
value:"具体内容请点击详情查看网站",
color:"#173177"
}
}
}
logger.info "start send template message: #{data}"
begin
req = Wechat.api.template_message_send Wechat::Message.to(uw.openid).template(data)
rescue Exception => e
logger.error "[blog_comment] ===> #{e}"
end
logger.info "send over. #{req}"
end
end
end
end

View File

@ -1,7 +1,10 @@
#encoding: utf-8
#老师布置的作业表
#homework_type: 0:普通作业;1:匿评作业;2:编程作业
class HomeworkCommon < ActiveRecord::Base
# attr_accessible :name, :user_id, :description, :publish_time, :end_time, :homework_type, :late_penalty, :course_id
require 'net/http'
require 'json'
include Redmine::SafeAttributes
include ApplicationHelper
@ -26,7 +29,7 @@ class HomeworkCommon < ActiveRecord::Base
:author => :author,
:url => Proc.new {|o| {:controller => 'student_work', :action => 'index', :homework => o.id}}
after_create :act_as_activity, :send_mail, :act_as_course_message
after_update :update_activity
after_update :update_activity, :wechat_message
after_save :act_as_course_activity
after_destroy :delete_kindeditor_assets
@ -98,6 +101,50 @@ class HomeworkCommon < ActiveRecord::Base
jfm
end
#修改作业后发送微信模板消息
def wechat_message
self.course.members.each do |member|
uw = UserWechat.where("user_id=?", member.user_id).first
unless uw.nil?
data = {
touser:uw.openid,
template_id:"3e5Dj2GIx8MOcMyRKpTUEQnM7Tg0ASSCNc01NS9HCGI",
url:"http://www.trustie.net/",
topcolor:"#FF0000",
data:{
first: {
value:"您的作业已被修改",
color:"#173177"
},
keyword1:{
value:self.course.name,
color:"#173177"
},
keyword2:{
value:self.name,
color:"#173177"
},
keyword3:{
value:self.end_time.to_s + "23:59:59",
color:"#173177"
},
remark:{
value:"具体内容请点击详情查看网站",
color:"#173177"
}
}
}
logger.info "start send template message: #{data}"
begin
req = Wechat.api.template_message_send Wechat::Message.to(uw.openid).template(data)
rescue Exception => e
logger.error "[homework_common] ===> #{e}"
end
logger.info "send over. #{req}"
end
end
end
delegate :language_name, :language, :to => :homework_detail_programing
end

View File

@ -1,3 +1,4 @@
#coding=utf-8
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
#
@ -16,6 +17,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Journal < ActiveRecord::Base
require 'net/http'
require 'json'
include UserScoreHelper
belongs_to :journalized, :polymorphic => true,:touch => true
# added as a quick fix to allow eager loading of the polymorphic association
@ -52,7 +55,7 @@ class Journal < ActiveRecord::Base
# fq
after_save :act_as_activity,:be_user_score, :act_as_forge_message, act_as_at_message(:notes, :user_id)
after_create :update_issue_time
after_create :issue_wechat_message
# end
#after_destroy :down_user_score
#before_save :be_user_score
@ -233,4 +236,47 @@ class Journal < ActiveRecord::Base
forge_activity.update_attribute(:created_at, self.created_on) unless forge_activity.nil?
end
end
#缺陷回复微信模板消息
def issue_wechat_message
uw = UserWechat.where(user_id: self.issue.author_id).first
#unless uw.nil? && self.issue.author_id != User.current.id
unless uw.nil?
data = {
touser:uw.openid,
template_id:"A_3f5v90-zK73V9Kijm-paDkl9S-NuM8Cf-1UJi92_c",
url:"http://www.trustie.net/",
topcolor:"#FF0000",
data:{
first: {
value:"您的缺陷有新回复了",
color:"#173177"
},
keyword1:{
value:self.user.try(:realname),
color:"#173177"
},
keyword2:{
value:format_time(self.created_on),
color:"#173177"
},
keyword3:{
value:self.notes.html_safe,
color:"#173177"
},
remark:{
value:"具体内容请点击详情查看网站",
color:"#173177"
}
}
}
logger.info "start send template message: #{data}"
begin
req = Wechat.api.template_message_send Wechat::Message.to(uw.openid).template(data)
rescue Exception => e
logger.error "[journal] ===> #{e}"
end
logger.info "send over. #{req}"
end
end
end

View File

@ -1,7 +1,9 @@
# fq
#coding=utf-8
# 数据库字段中带有m前缀和is_readed是二次开发添加之前的字段基本复用
# 注意reply_id 是提到人的id不是留言id, Base中叫做 at_user
class JournalsForMessage < ActiveRecord::Base
require 'net/http'
require 'json'
include Redmine::SafeAttributes
include UserScoreHelper
include ApplicationHelper
@ -253,6 +255,9 @@ class JournalsForMessage < ActiveRecord::Base
self.course_messages << CourseMessage.new(:user_id => r, :course_id => self.jour.id, :viewed => false)
end
end
if self.jour_type == 'HomeworkCommon'
journal_wechat_message '您的作业有新回复了',self.jour.user_id
end
end
@ -264,6 +269,7 @@ class JournalsForMessage < ActiveRecord::Base
if self.reply_id == 0
if self.user_id != self.jour_id # 过滤自己给自己的留言消息
receivers << self.jour
journal_wechat_message "您有新留言了",self.jour_id
end
else # 留言回复
reply_to = User.find(self.reply_id)
@ -273,6 +279,7 @@ class JournalsForMessage < ActiveRecord::Base
if self.user_id != self.parent.jour_id && self.reply_id != self.parent.jour_id # 给东家发信息,如果回复的对象是东家则不发
receivers << self.parent.jour
end
journal_wechat_message "您的留言有新回复了",self.reply_id
end
receivers.each do |r|
self.user_feedback_messages << UserFeedbackMessage.new(:user_id => r.id, :journals_for_message_id => self.id, :journals_for_message_type => "Principal", :viewed => false)
@ -299,4 +306,47 @@ class JournalsForMessage < ActiveRecord::Base
end
end
#微信模板消息
def journal_wechat_message type, user_id
uw = UserWechat.where(user_id: user_id).first
#unless uw.nil? && self.reply_id != User.current.id
unless uw.nil?
data = {
touser:uw.openid,
template_id:"A_3f5v90-zK73V9Kijm-paDkl9S-NuM8Cf-1UJi92_c",
url:"http://www.trustie.net/",
topcolor:"#FF0000",
data:{
first: {
value:type,
color:"#173177"
},
keyword1:{
value:self.user.try(:realname),
color:"#173177"
},
keyword2:{
value:format_time(self.created_on),
color:"#173177"
},
keyword3:{
value:self.notes.html_safe,
color:"#173177"
},
remark:{
value:"具体内容请点击详情查看网站",
color:"#173177"
}
}
}
logger.info "start send template message: #{data}"
begin
req = Wechat.api.template_message_send Wechat::Message.to(uw.openid).template(data)
rescue Exception => e
logger.error "[journal_for_message] ===> #{e}"
end
logger.info "send over. #{req}"
end
end
end

View File

@ -1,3 +1,4 @@
#encoding: utf-8
# Redmine - project management software
# Copyright (C) 2006-2013 Jean-Philippe Lang
#
@ -16,6 +17,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class Mailer < ActionMailer::Base
require 'net/http'
require 'json'
layout 'mailer'
helper :application
helper :issues
@ -626,6 +629,7 @@ class Mailer < ActionMailer::Base
# attachments_added(attachments) => Mail::Message object
# Mailer.attachments_added(attachments).deliver => sends an email to the project's recipients
def homework_added(homework_common)
logger.info "homework added"
@homework_common = homework_common
@author = homework_common.user
@homework_common_url = url_for(:controller => "homework_common", :action =>"index", :homework => @homework_common.id)
@ -638,6 +642,9 @@ class Mailer < ActionMailer::Base
mail :to => recipients,
:subject => "[ #{l(:label_user_homework)} : #{homework_common.name} #{l(:label_memo_create_succ)}]",
:filter => true
@homework_common.course.members.each do |member|
mail_wechat_message member.user_id, "3e5Dj2GIx8MOcMyRKpTUEQnM7Tg0ASSCNc01NS9HCGI", "您的课程有新作业了", @homework_common.course.name, @homework_common.name, @homework_common.end_time.to_s + " 23:59:59"
end
end
# Builds a Mail::Message object used to email recipients of a news' project when a news item is added.
@ -703,6 +710,8 @@ class Mailer < ActionMailer::Base
mail :to => recipients,
:subject => "[#{news.course.name}] #{l(:label_news)}: #{news.title}",
:filter => true
mail_wechat_message news.author_id, "A_3f5v90-zK73V9Kijm-paDkl9S-NuM8Cf-1UJi92_c", "您的课程通知有新回复了", @author.try(:realname), format_time(comment.created_on), comment.comments.html_safe
end
end
@ -727,6 +736,13 @@ class Mailer < ActionMailer::Base
:cc => cc,
:subject => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}",
:filter => true
if message.parent_id == nil
message.project.members.each do |member|
mail_wechat_message member.user_id, "oKzFCdk7bsIHnGbscA__N8LPQrBkUShvpjV3-kuwWDQ", "项目讨论区有新帖子发布了", message.subject, @author.try(:realname), format_time(message.created_on)
end
else
mail_wechat_message message.parent.author_id, "A_3f5v90-zK73V9Kijm-paDkl9S-NuM8Cf-1UJi92_c", "您的帖子有新回复了", @author.try(:realname), format_time(message.created_on), message.content.html_safe
end
elsif message.course
redmine_headers 'Course' => message.course.id,
'Topic-Id' => (message.parent_id || message.id)
@ -742,6 +758,13 @@ class Mailer < ActionMailer::Base
:cc => cc,
:subject => "[#{message.board.course.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}",
:filter => true
if message.parent_id == nil
message.course.members.each do |member|
mail_wechat_message member.user_id, "oKzFCdk7bsIHnGbscA__N8LPQrBkUShvpjV3-kuwWDQ", "课程问答区有新帖子发布了", message.subject, @author.try(:realname), format_time(message.created_on)
end
else
mail_wechat_message message.parent.author_id, "A_3f5v90-zK73V9Kijm-paDkl9S-NuM8Cf-1UJi92_c", "您的帖子有新回复了", @author.try(:realname), format_time(message.created_on), message.content.html_safe
end
end
end
@ -1097,4 +1120,47 @@ class Mailer < ActionMailer::Base
return newpass
end
#微信模板消息
def mail_wechat_message user_id, template_id, first, key1, key2, key3, remark="具体内容请点击详情查看网站"
uw = UserWechat.where(user_id: user_id).first
logger.info "mail_wechat_message #{user_id} #{uw}"
unless uw.nil?
data = {
touser:uw.openid,
template_id:template_id,
url:"http://www.trustie.net/",
topcolor:"#FF0000",
data:{
first: {
value:first,
color:"#173177"
},
keyword1:{
value:key1,
color:"#173177"
},
keyword2:{
value:key2,
color:"#173177"
},
keyword3:{
value:key3,
color:"#173177"
},
remark:{
value:remark,
color:"#173177"
}
}
}
logger.info "start send template message: #{data}"
begin
req = Wechat.api.template_message_send Wechat::Message.to(uw.openid).template(data)
rescue Exception => e
logger.error "[mailer] ===> #{e}"
end
logger.info "send over. #{req}"
end
end
end

View File

@ -179,6 +179,7 @@ class User < Principal
#####
has_many :shares ,:dependent => :destroy
has_one :user_wechat
# add by zjc
has_one :level, :class_name => 'UserLevels', :dependent => :destroy
@ -1181,17 +1182,17 @@ class User < Principal
def create_user_ealasticsearch_index
if self.id != 2 && self.id != 4
self.__elasticsearch__.index_document
self.__elasticsearch__.index_document if Rails.env.production?
end
end
def update_user_ealasticsearch_index
if self.id != 2 && self.id != 4
self.__elasticsearch__.update_document
self.__elasticsearch__.update_document if Rails.env.production?
end
end
def delete_user_ealasticsearch_index
if self.id != 2 && self.id != 4
self.__elasticsearch__.delete_document
self.__elasticsearch__.delete_document if Rails.env.production?
end
end

View File

@ -0,0 +1,6 @@
class UserWechat < ActiveRecord::Base
attr_accessible :subscribe, :openid, :nickname, :sex, :language, :city, :province, :country,
:headimgurl, :subscribe_time, :unionid, :remark, :groupid, :user, :user_id
belongs_to :user
end

View File

@ -23,10 +23,8 @@
<!--<input type="submit" name="" value="上传文件" class="f_l ml10" style="width:80px; height:26px;">-->
<span id="upload_file_count<%=container.id %>">
<%= l(:label_no_file_uploaded)%>
建议上传高度不超过52px的图片
</span>
(<%= l(:label_max_size) %>:<%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)
<p style="padding-left: 68px;">建议上传高度不超过52px的图片</p>
<div class="cl"></div>
<div>
<span id="attachments_fields<%= container.id %>" data-containerid="<%= container.id %>" xmlns="http://www.w3.org/1999/html">

View File

@ -0,0 +1,32 @@
<span class="add_attachment" data-containerid="<%= container.id %>">
<button name="button" class="sub_btn" onclick="_file<%=container.id %>.click()" onmouseover="this.focus()" style="<%= ie8? ? 'display:none' : ''%>" type="button" ><%= l(:label_browse_org) %></button>
<%= file_field_tag 'attachments[dummy][file]',
:id => "_file#{container.id}",
:class => ie8? ? '':'file_selector',
:multiple => true,
:onchange => "addInputFiles_board(this, '#{container.id}','"+"submit_resource"+"');",
: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)),
:max_concurrent_uploads => Redmine::Configuration['max_concurrent_ajax_uploads'].to_i,
:upload_path => uploads_path(:format => 'js'),
:description_placeholder => l(:label_optional_description),
:field_is_public => l(:field_is_public),
:are_you_sure => l(:text_are_you_sure),
:file_count => l(:label_file_count),
:delete_all_files => l(:text_are_you_sure_all),
:lebel_file_uploding => l(:lebel_file_uploding),
:containerid => "#{container.id}"
} %>
</span>
<!--<input type="submit" name="" value="上传文件" class="f_l ml10" style="width:80px; height:26px;">-->
<span id="upload_file_count<%=container.id %>">
建议上传 长度:1920px 高度:313px 的图片
</span>
<div class="cl"></div>
<div>
<span id="attachments_fields<%= container.id %>" data-containerid="<%= container.id %>" xmlns="http://www.w3.org/1999/html">
</span>
</div>

View File

@ -0,0 +1,25 @@
<div id="popbox_upload" class="mb10" style="margin-top: -30px;color:#15bccf; font-size:16px;">
<div class="upload_con">
<h2 style="text-align: center">更换背景图片</h2>
<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(organization_files_path(org, :in_org => params[:in_org]), :multipart => true,:remote => !ie8?,:name=>"upload_form") do %>
<!--<input type="hidden" name="org_subfield_attachment_type" value="<%#= org_subfield_attachment_type%>">-->
<%= render :partial => 'files/org_upload_attachment_list_banner', :locals => {:container => org}%>
<div class="cl"></div>
<a href="javascript:void(0);" class=" fr grey_btn mr40" onclick="hideModal();"><%= l(:button_cancel)%></a>
<a id="submit_org_resource" href="javascript:void(0);" class="blue_btn fr" onclick="submit_resource();"><%= l(:button_confirm)%></a>
<% end %>
</div>
</div>
</div>
<script>
function submit_resource()
{
$('#submit_org_resource').parent().submit();
}
</script>

View File

@ -62,9 +62,19 @@
<!--add by huang-->
<script type="text/javascript">
function orge_new_files_upload()
function org_new_files_upload()
{
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'files/upload_org_new_files',:locals => {:org => @organization, :org_attachment_type => 1}) %>');
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'files/upload_org_new_files',:locals => {:org => @organization, :org_attachment_type => 0}) %>');
showModal('ajax-modal', '513px');
$('#ajax-modal').siblings().remove();
$('#ajax-modal').before("<a href='javascript:void(0)' onclick='hideModal()' style='margin-left: 480px;'><img src='/images/bid/close.png' width='26px' height='26px' /></a>");
$('#ajax-modal').parent().css("top","40%").css("left","36%").css("border","3px solid #269ac9");
$('#ajax-modal').parent().addClass("popbox_polls");
}
function org_new_files_banner_upload()
{
$('#ajax-modal').html('<%= escape_javascript(render :partial => 'files/upload_org_new_files_banner',:locals => {:org => @organization, :org_attachment_type => 1}) %>');
showModal('ajax-modal', '513px');
$('#ajax-modal').siblings().remove();
$('#ajax-modal').before("<a href='javascript:void(0)' onclick='hideModal()' style='margin-left: 480px;'><img src='/images/bid/close.png' width='26px' height='26px' /></a>");
@ -78,64 +88,83 @@
<div class="header">
<div class="header-con">
<% if User.current.admin_of_org?(@organization) %>
<a href="javascript:void(0);" class="fl logo" onclick="orge_new_files_upload();">
<!--"/files/uploads/image#{iamge_path}"-->
<div class="logo fl">
<% if @org_logo_attchment.blank? %>
<img src="/images/org_new_style/logo.jpg" height="52" alt=""/>
<a class="logo-img"><img src="/images/org_new_style/logo.jpg" alt=""/></a>
<% else %>
<img src="/attachments/<%= @org_logo_attchment.id %>/<%= @org_logo_attchment.filename %>" height="52">
<img src="/attachments/<%= @org_logo_attchment.id %>/<%= @org_logo_attchment.filename %>" class="logo-img">
<% end %>
</a>
<a href="#" class="logo-add" title="点击替换LOGO" onclick="org_new_files_upload();"></a>
</div>
<% else %>
<a href="javascript:void(0);" class="fl logo" onclick="orge_new_files_upload();">
<!--"/files/uploads/image#{iamge_path}"-->
<div class="logo fl">
<% if @org_logo_attchment.blank? %>
<img src="/images/org_new_style/logo.jpg" height="52" alt=""/>
<a class="logo-img"><img src="/images/org_new_style/logo.jpg" alt=""/></a>
<% else %>
<img src="/attachments/<%= @org_logo_attchment.id %>/<%= @org_logo_attchment.filename %>" height="52">
<img src="/attachments/<%= @org_logo_attchment.id %>/<%= @org_logo_attchment.filename %>" class="logo-img">
<% end %>
</a>
</div>
<% end %>
<%# 登录 %>
<%= render :partial => 'organizations/org_logined_header' %>
</div>
<div class="cl"></div>
</div><!--header end-->
<div class="nav-box">
<div class="nav fl">
<% @subfield_content.each do |field| %>
<% if is_default_field?(field) %>
<% case field.name %>
<% when 'activity' %>
<%= link_to "首页", organization_path(@organization), :class => "fl navact" %>
<% when 'course' %>
<a href="#course_<%= field.id %>" class="fl"> 课程动态</a>
<% when 'project' %>
<a href="#project_<%= field.id %>" class="fl">项目动态</a>
<% end %>
<% else %>
<% if field.field_type == "Post" && field.hide == 0 %>
<a href="#message_<%= field.id %>" class="fl"><%= field.name %></a>
<% elsif field.field_type == "Resource" && field.hide == 0 %>
<a href="#resource_<%= field.id %>" class="fl"><%= field.name %></a>
<% end %>
<% end %>
<% end %>
<% if User.current.admin_of_org?(@organization) %>
<a href="<%= setting_organization_path(@organization) %>" class="fl">配置</a>
<% end %>
<div class="nav-con">
<div class="nav fl">
<% @subfield_content.each do |field| %>
<% if is_default_field?(field) %>
<% case field.name %>
<% when 'activity' %>
<%= link_to "首页", organization_path(@organization), :class => "fl navact" %>
<% when 'course' %>
<a href="#course_<%= field.id %>" class="fl"> 课程动态</a>
<% when 'project' %>
<a href="#project_<%= field.id %>" class="fl">项目动态</a>
<% end %>
<% else %>
<% if field.field_type == "Post" && field.hide == 0 %>
<a href="#message_<%= field.id %>" class="fl"><%= field.name %></a>
<% elsif field.field_type == "Resource" && field.hide == 0 %>
<a href="#resource_<%= field.id %>" class="fl"><%= field.name %></a>
<% end %>
<% end %>
<% end %>
<% if User.current.admin_of_org?(@organization) %>
<a href="<%= setting_organization_path(@organization) %>" class="fl">配置</a>
<% end %>
</div>
<div class="cl"></div>
</div>
</div><!--nav end-->
</div>
<!--nav end-->
<div class="banner">
<h2><%= @organization.name %></h2>
<div class="banner-inner">
<% if User.current.admin_of_org?(@organization) %>
<% if @org_banner_attchment.blank? %>
<img class="banner-img" src="/images/org_new_style/banner.jpg" alt=""/>
<% else %>
<img src="/attachments/<%= @org_banner_attchment.id %>/<%= @org_banner_attchment.filename %>" class="banner-img">
<% end %>
<a href="#" class="banner-add" title="点击替换图片" onclick="org_new_files_banner_upload();"></a>
<div class="banner-txt">
<p ><%= @organization.name %></p>
</div>
<% else %>
<% if @org_banner_attchment.blank? %>
<img class="banner-img" src="/images/org_new_style/banner.jpg" alt=""/>
<% else %>
<img src="/attachments/<%= @org_banner_attchment.id %>/<%= @org_banner_attchment.filename %>" class="banner-img">
<% end %>
<div class="banner-txt">
<p ><%= @organization.name %></p>
</div>
<% end %>
</div>
</div>
<div class="cl"></div>
<!--模块-->
<% @subfield_content.each do |field| %>
<% if is_default_field?(field) %>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<%= csrf_meta_tag %>
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
<title>绑定用户</title>
<link rel="stylesheet" href="/stylesheets/weui/weui.min.css"/>
<script src="/javascripts/jquery.min.js"></script>
<script src="/javascripts/wechat/alert.js"></script>
</head>
<%= yield %>
<!--BEGIN dialog2-->
<div class="weui_dialog_alert" id="dialog2" style="display: none;">
<div class="weui_mask"></div>
<div class="weui_dialog">
<div class="weui_dialog_hd"><strong class="weui_dialog_title">弹窗标题</strong></div>
<div class="weui_dialog_bd"><span class="weui_dialog_info">弹窗内容,告知当前页面信息等</span></div>
<div class="weui_dialog_ft">
<a href="javascript:;" class="weui_btn_dialog primary">确定</a>
</div>
</div>
</div>
<!--END dialog2-->
<!--BEGIN dialog1-->
<div class="weui_dialog_confirm" id="dialog1" style="display: none;">
<div class="weui_mask"></div>
<div class="weui_dialog">
<div class="weui_dialog_hd"><strong class="weui_dialog_title">弹窗标题</strong></div>
<div class="weui_dialog_bd"><span class="weui_dialog_info">自定义弹窗内容,居左对齐显示,告知需要确认的信息等</span></div>
<div class="weui_dialog_ft">
<a href="javascript:;" class="weui_btn_dialog default cancel">取消</a>
<a href="javascript:;" class="weui_btn_dialog primary confirm">确定</a>
</div>
</div>
</div>
<!--END dialog1-->
</body>
</html>

View File

@ -24,7 +24,8 @@
<body>
<!--<div class="resourceSharePopup">-->
<div>
<div class="relateText fl">请选择关联到组织的课程</div>
<div class="relateText">请选择关联到组织的课程</div>
<div class="c_red">您的私有课程不能被关联到组织</div>
</div>
<div class="fl">
<%=form_tag url_for(:controller => 'organizations', :action => 'join_courses', :organization_id => organization_id),:method => 'post', :id => 'join_courses_form', :remote => true,:class=>"resourcesSearchBox" do %>

View File

@ -24,7 +24,8 @@
<body>
<div>
<div class="relateText fl">请选择关联到组织的项目</div>
<div class="relateText">请选择关联到组织的项目</div>
<div class="c_red">您的私有项目不能被关联到组织</div>
</div>
<div class="fl">
<%=form_tag url_for(:controller => 'organizations', :action => 'join_projects', :organization_id => organization_id),:method => 'post', :id => 'join_projects_form', :remote => true,:class=>"resourcesSearchBox" do %>

View File

@ -138,7 +138,7 @@
</li>
<% roles.each do |role| %>
<li>
<%= radio_button_tag 'membership[role_ids][]', role.id %>
<%= radio_button_tag 'membership[role_ids][]', role.id, role.name == "报告人员" || role.name == "Reporter" %>
<% if User.current.language == "zh" %>
<% if role.id == 3 %>
<label >管理人员</label>

View File

@ -7,8 +7,7 @@
</span>
<%= link_to('&nbsp;'.html_safe, attachment_path(attachment, :format => 'js'), :method => 'delete', :remote => true, :title => '删除', :class => 'remove-upload fl', :confirm => l(:text_are_you_sure)) if attachment.id && User.current == attachment.author && status != 2 %>
<span class="postAttSize">(<%= number_to_human_size attachment.filesize %>)</span>
<span class="author" title="<%= attachment.author%>">
<%= link_to h(truncate(attachment.author.name, length: 10, omission: '...')),user_path(attachment.author),:class => "c_orange" %>,
<span class="author">
<%= format_time(attachment.created_on) %>
</span>
<div class="cl"></div>

View File

@ -145,23 +145,25 @@
</div>
<% end %>
<div class="homepagePostReplyContainer borderBottomNone minHeight48">
<div class="homepagePostReplyPortrait mr15 imageFuzzy" id="reply_image_<%= user_activity_id%>"><%= link_to image_tag(url_to_avatar(User.current), :width => "33", :height => "33"), user_path(activity.author_id), :alt => "用户头像" %></div>
<div class="homepagePostReplyInputContainer mb10">
<div nhname='new_message_<%= user_activity_id%>' style="display:none;">
<%= form_for('new_form',:url => {:controller=>'messages',:action => 'reply', :id => activity.id, :board_id => activity.board_id, :is_board => 'true'},:method => "post", :remote => true) do |f|%>
<input type="hidden" name="quote[quote]" value="">
<input type="hidden" name="user_activity_id" value="<%=user_activity_id%>">
<div nhname='toolbar_container_<%= user_activity_id%>'></div>
<textarea placeholder="有问题或有建议,请直接给我留言吧!" style="display: none" nhname='new_message_textarea_<%= user_activity_id%>' name="reply[content]"></textarea>
<a id="new_message_submit_btn_<%= user_activity_id%>" href="javascript:void(0)" onclick="this.style.display='none'" class="blue_n_btn fr" style="display:none;margin-top:6px;">发送</a>
<div class="cl"></div>
<p nhname='contentmsg_<%= user_activity_id%>'></p>
<% end%>
<% if !activity.locked? %>
<div class="homepagePostReplyContainer borderBottomNone minHeight48">
<div class="homepagePostReplyPortrait mr15 imageFuzzy" id="reply_image_<%= user_activity_id%>"><%= link_to image_tag(url_to_avatar(User.current), :width => "33", :height => "33"), user_path(activity.author_id), :alt => "用户头像" %></div>
<div class="homepagePostReplyInputContainer mb10">
<div nhname='new_message_<%= user_activity_id%>' style="display:none;">
<%= form_for('new_form',:url => {:controller=>'messages',:action => 'reply', :id => activity.id, :board_id => activity.board_id, :is_board => 'true'},:method => "post", :remote => true) do |f|%>
<input type="hidden" name="quote[quote]" value="">
<input type="hidden" name="user_activity_id" value="<%=user_activity_id%>">
<div nhname='toolbar_container_<%= user_activity_id%>'></div>
<textarea placeholder="有问题或有建议,请直接给我留言吧!" style="display: none" nhname='new_message_textarea_<%= user_activity_id%>' name="reply[content]"></textarea>
<a id="new_message_submit_btn_<%= user_activity_id%>" href="javascript:void(0)" onclick="this.style.display='none'" class="blue_n_btn fr" style="display:none;margin-top:6px;">发送</a>
<div class="cl"></div>
<p nhname='contentmsg_<%= user_activity_id%>'></p>
<% end%>
</div>
<div class="cl"></div>
</div>
<div class="cl"></div>
</div>
<div class="cl"></div>
</div>
<div class="cl"></div>
</div>
<% end %>
</div>
</div>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
<title>绑定用户</title>
<link rel="stylesheet" href="/stylesheets/weui/weui.min.css"/>
<script type="text/javascript">
function close(){
WeixinJSBridge.call('closeWindow');
}
</script>
</head>
<body>
<div class="page">
<div class="weui_msg">
<div class="weui_icon_area"><i class="weui_icon_success weui_icon_msg"></i></div>
<div class="weui_text_area">
<h2 class="weui_msg_title">操作成功</h2>
<p class="weui_msg_desc">您已成功绑定微信</p>
</div>
<div class="weui_opr_area">
<p class="weui_btn_area">
<a href="javascript:close();" class="weui_btn weui_btn_primary">确定</a>
</p>
</div>
</div>
</div>
</body>
</html>

View File

View File

@ -0,0 +1,68 @@
<div class="loginIn">
<div>
<p class="weui_cells_title wechat-error">
<%= @wechat_bind_errors %>
</p>
</div>
<%= form_tag(bind_wechat_path,:id=>'main_login_form',:method=>'post') do %>
<div class="weui_cells weui_cells_form">
<div class="weui_cell">
<div class="weui_cell_hd"><label class="weui_label">用户名</label></div>
<div class="weui_cell_bd weui_cell_primary">
<input class="weui_input" autocapitalize="off" type="text" name="username" placeholder="请输入邮箱地址或昵称"/>
</div>
</div>
<div class="weui_cell">
<div class="weui_cell_hd"><label class="weui_label">用户名</label></div>
<div class="weui_cell_bd weui_cell_primary">
<input class="weui_input" type="password" name="password" placeholder="请输密码"/>
</div>
</div>
<input type="hidden" value="<%=@code%>" name="code">
<div class="weui_btn_area">
<a class="weui_btn weui_btn_primary" id="submitForm">确定</a>
</div>
</div>
<% end %>
</div>
<script type="text/javascript">
$(function(){
console.log("started.");
$("#submitForm").click(function(){
var data = {};
$("#main_login_form").serializeArray().map(function(x){data[x.name] = x.value;});
console.log(data);
$.ajax({
type: "POST",
url: $("#main_login_form").attr("action"),
data:data,
dataType: 'json',
success: function(data){
console.log(data);
if(data.status == 0){
byConfirm(data.msg, function(){
window.closeWindow();
});
} else {
byAlert(data.msg, "绑定失败");
}
}
});
})
});
</script>

View File

@ -69,6 +69,14 @@ module RedmineApp
config.action_view.sanitized_allowed_tags = 'div', 'p', 'span', 'img', 'embed'
config.middleware.use Rack::Cors do
allow do
origins '*'
# location of your API
resource '/api/*', :headers => :any, :methods => [:get, :post, :options, :put]
end
end
config.before_initialize do
end
@ -91,4 +99,4 @@ module RedmineApp
end
end
end
end

View File

@ -0,0 +1,3 @@
class ActiveRecord::ConnectionAdapters::Mysql2Adapter
NATIVE_DATABASE_TYPES[:primary_key] = "int(11) auto_increment PRIMARY KEY"
end

20
config/menu.yml Normal file
View File

@ -0,0 +1,20 @@
button:
-
type: "view"
name: "最新动态"
url: "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxc09454f171153c2d&redirect_uri=http://wechat.trustie.net/assets/wechat/app.html#/activities?response_type=code&scope=snsapi_base&state=123#wechat_redirect"
-
type: "click"
name: "意见返馈"
key: "FEEDBACK"
-
name: "更多"
sub_button:
-
type: "view"
name: "进入网站"
url: "http://www.trustie.net/"
-
type: "view"
name: "使用手册"
url: "https://www.trustie.net/organizations/1/downloads"

View File

@ -1170,6 +1170,13 @@ RedmineApp::Application.routes.draw do
end
resources :at
resource :wechat, only:[:show, :create] do
collection do
get :login
post :bind
post :get_open_id
end
end
get '/:sub_dir_name', :to => 'org_subfields#show', :as => 'show_subfield_without_id'

21
config/wechat.yml Normal file
View File

@ -0,0 +1,21 @@
default: &default
# corpid: "corpid"
# corpsecret: "corpsecret"
# agentid: 1
# Or if using public account, only need above two line
appid: "wxc09454f171153c2d"
secret: "dff5b606e34dcafe24163ec82c2715f8"
token: "123456"
access_token: "1234567"
encrypt_mode: false # if true must fill encoding_aes_key
encoding_aes_key: "QyocNOkRmrT5HzBpCG54EVPUQjk86nJapXNVDQm6Yy6"
jsapi_ticket: "C:/Users/[user_name]/wechat_jsapi_ticket"
production:
<<: *default
development:
<<: *default
test:
<<: *default

View File

@ -0,0 +1,11 @@
class CreateWechatLogs < ActiveRecord::Migration
def change
create_table :wechat_logs do |t|
t.string :openid, null: false, index: true
t.text :request_raw
t.text :response_raw
t.text :session_raw
t.datetime :created_at, null: false
end
end
end

View File

@ -0,0 +1,21 @@
class CreateUserWechats < ActiveRecord::Migration
def change
create_table :user_wechats do |t|
t.integer :subscribe
t.string :openid
t.string :nickname
t.integer :sex
t.string :language
t.string :city
t.string :province
t.string :country
t.string :headimgurl
t.string :subscribe_time
t.string :unionid
t.string :remark
t.integer :groupid
t.references :user
t.timestamps
end
end
end

View File

@ -0,0 +1,21 @@
class DeleteBlogUserActivity < ActiveRecord::Migration
def up
UserActivity.all.each do |activity|
if activity.act_type == 'BlogComment'
if activity.act
unless activity.act.parent_id.nil?
parent_act = UserActivity.where("act_id = #{activity.act.parent.id} and act_type='BlogComment'").first
parent_act.created_at = activity.act.parent.children.maximum("created_on")
parent_act.save
activity.destroy
end
else
activity.destroy
end
end
end
end
def down
end
end

File diff suppressed because it is too large Load Diff

View File

@ -140,6 +140,7 @@ module Redmine
if file && file.size > 0
a = Attachment.create(:file => file, :author => author)
elsif token
# 通过token值找到对应的attachment
a = Attachment.find_by_token_only(token)
if a
a.filename = attachment['filename'] unless attachment['filename'].blank?

23
public/app.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>react js</title>
<meta charset='utf-8' />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta name="apple-mobile-web-app-capable" content="no">
<meta content='True' name='HandheldFriendly' />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link type="text/css" rel="stylesheet" href="/stylesheets/weui/weixin.css" />
<script src="/javascripts/wechat/react.js"></script>
<script src="/javascripts/wechat/JSXTransformer.js"></script>
<script src="/javascripts/wechat/ReactRouter.js"></script>
</head>
<body>
<div id="container"></div>
<script src="/javascripts/wechat/jquery.min.js"></script>
<script type="text/jsx" src="/javascripts/wechat/wechat.jsx"></script>
</body>
</html>

View File

@ -0,0 +1,223 @@
<div id="container">
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div ng-repeat="act in activities">
<div ng-if="act.container_type=='Course'">
<div ng-if="act.act_type=='HomeworkCommon'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.subject|safeHtml}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{act.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;{{act.activity_type_name}}</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content" ng-bind-html="act.description|safeHtml"></div>
<span class="mr15">迟交扣分:{{act.homework_common_detail.late_penalty}}分</span> 匿评开启时间:{{act.homework_common_detail.evaluation_start}}<br />
<span class="mr15">缺评扣分:{{act.homework_common_detail.absence_penalty}}分/作品</span> 匿评关闭时间:{{act.homework_common_detail.evaluation_end}}
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{act.latest_update}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/homework/{{act.act_id}}" class="c-grey">回复 ({{act.reply_count}})</a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
<div ng-if="act.act_type=='News'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.subject|safeHtml}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{act.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;{{act.activity_type_name}}</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content" ng-bind-html="act.description|safeHtml"></div>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{act.latest_update}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/course_notice/{{act.act_id}}" class="c-grey">回复 ({{act.reply_count}})</a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
<div ng-if="act.act_type=='Message'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.subject|safeHtml}}</span></div>
<div class="post-title fl mb10 hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{act.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;{{act.activity_type_name}}</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content" ng-bind-html="act.description|safeHtml"></div>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{act.latest_update}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/course_discussion/{{act.act_id}}" class="c-grey">回复 ({{act.reply_count}})</a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
<div ng-if="act.act_type=='Course'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f13 fb mr10">{{act.author.realname}}</span>创建了<span class="c-grey3 f13 fb ml10">{{act.course_project_name}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.latest_update}}</a></div>
<div class="cl"></div>
</div>
</div>
</div>
</div>
</div>
<div ng-if="act.container_type=='Project'">
<div ng-if="act.act_type=='Issue'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.subject|safeHtml}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{act.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;{{act.activity_type_name}}</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content" ng-bind-html="act.description|safeHtml"></div>
<span class="mr15">状态:{{act.issue_detail.issue_status}}</span> <span class="mr15">优先级:{{act.issue_detail.issue_priority}}</span> <br />
<span class="mr15">指派给:{{act.issue_detail.issue_assigned_to}}</span> <span class="mr15">完成度:{{act.issue_detail.done_ratio}}%</span>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{act.latest_update}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/issues/{{act.act_id}}" class="c-grey"> 回复 ({{act.reply_count}})</a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
<div ng-if="act.act_type=='Message'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.subject|safeHtml}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{act.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;{{act.activity_type_name}}</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content" ng-bind-html="act.description|safeHtml"></div>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{act.latest_update}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/project_discussion/{{act.act_id}}" class="c-grey"> 回复 ({{act.reply_count}}) </a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
<div ng-if="act.act_type=='ProjectCreateInfo'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.author.realname}}</span>创建了<span class="c-grey3 f15 fb">{{act.course_project_name}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.latest_update}}</a></div>
<div class="cl"></div>
</div>
</div>
</div>
</div>
</div>
<div ng-if="act.container_type=='Principal'">
<div ng-if="act.act_type=='JournalsForMessage'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title mb5 hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a> <span style="vertical-align:top;">给您留言了</span></div>
<div class="post-title hidden">{{act.latest_update}}</div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<p class="post-all-content" ng-bind-html="act.description|safeHtml"></p>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/journal_for_message/{{act.act_id}}" class="c-grey"> 回复 ({{act.reply_count}})</a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
</div>
<div ng-if="act.act_type=='BlogComment'">
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{replaceUrl(act.author.img_url)}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{act.subject|safeHtml}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{act.author.realname}}</a>发表博客</div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content" ng-bind-html="act.description|safeHtml"></div>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more undis underline" text-auto-height>点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{act.latest_update}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive">
<div class="post-interactive-column c-grey2"><a href="javascript:void(0);" ng-href="#/blog_comment/{{act.act_id}}" class="c-grey"> 回复 ({{act.reply_count}})</a></div>
<div class="post-interactive-column c-grey2" ng-if="!act.has_praise" ng-click="addPraise(act);">赞 ({{act.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="act.has_praise" ng-click="decreasePraise(act);">已赞 ({{act.praise_count}})</div>
</div>
</div>
</div>
</div>
</div>
<div ng-if="(count + page * 10) < all_count">
<div id="more_activities" class="more-events mt10" ng-click="loadActData(current_page+1);">更多</div>
</div>
</div>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html ng-app="wechat">
<head>
<title>最新动态</title>
<meta charset='utf-8' />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta name="apple-mobile-web-app-capable" content="no">
<meta content='True' name='HandheldFriendly' />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link type="text/css" rel="stylesheet" href="/stylesheets/weui/weixin.css" />
</head>
<body>
<div ng-view></div>
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.js"></script>
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular-route.js"></script>
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular-sanitize.min.js"></script>
<script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular-cookies.js"></script>
<script src="/javascripts/jquery-1.3.2.js"></script>
<script src="/javascripts/wechat/app.js"></script>
</body>
</html>

View File

@ -0,0 +1,45 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{blog.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{blog.title}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{blog.user.realname}}</a>发表博客</div>
<div class="cl"></div>
<div class="post-content" style="height:auto;">
<div class="post-all-content c-grey2 mt10" ng-bind-html="blog.content|safeHtml"></div>
</div>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{blog.created_at}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{blog.comment_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="blog.has_praise" ng-click="decreasePraise(blog);">已赞 ({{blog.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!blog.has_praise" ng-click="addPraise(blog);">赞 ({{blog.praise_count}})</div>
</div>
<div id="all_blog_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="journal in blog.blog_comment_children">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{journal.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{journal.user.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="journal.content|safeHtml"></div>
<div class="post-reply-date fl">{{journal.lasted_comment}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addBlogReply(formData)" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{discussion.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{discussion.subject}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{discussion.user.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{discussion.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;课程问答区</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10" style="height:auto;">
<div class="post-all-content" ng-bind-html="discussion.content|safeHtml"></div>
</div>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{discussion.created_on}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{discussion.replies_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="discussion.has_praise" ng-click="decreasePraise(discussion);">已赞 ({{discussion.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!discussion.has_praise" ng-click="addPraise(discussion);">赞 ({{discussion.praise_count}})</div>
</div>
<div id="all_course_message_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="journal in discussion.message_children">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{journal.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{journal.user.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="journal.content|safeHtml"></div>
<div class="post-reply-date fl">{{journal.lasted_comment}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addDiscussionReply(formData)" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{news.author.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{news.title}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{news.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{news.course_name}}&nbsp;&nbsp;|&nbsp;&nbsp;课程通知</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10" style="height:auto;">
<div class="post-all-content" ng-bind-html="news.description|safeHtml"></div>
</div>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{news.created_on}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{news.comments_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="news.has_praise" ng-click="decreasePraise(news);">已赞 ({{news.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!news.has_praise" ng-click="addPraise(news);">赞 ({{news.praise_count}})</div>
</div>
<div id="all_news_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="comments in news.comments">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{comments.author.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{comments.author.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="comments.comments|safeHtml"></div>
<div class="post-reply-date fl">{{comments.created_on}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addNoticeReply(formData)" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,47 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{homework.author.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{homework.name}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{homework.author.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{homework.course_name}}&nbsp;&nbsp;|&nbsp;&nbsp;课程作业</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10" style="height:auto;">
<div class="post-all-content" ng-bind-html="homework.description|safeHtml"></div>
<span class="mr15">迟交扣分:{{homework.late_penalty}}分</span> 匿评开启时间:{{homework.evaluation_start}}<br />
<span class="mr15">缺评扣分:{{homework.absence_penalty}}分/作品</span> 匿评关闭时间:{{homework.evaluation_end}}
</div>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{homework.publish_time}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{homework.whomework_journal_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="homework.has_praise" ng-click="decreasePraise(homework);">已赞 ({{homework.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!homework.has_praise" ng-click="addPraise(homework);">赞 ({{homework.praise_count}})</div>
</div>
<div id="all_homework_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="journal in homework.journals_for_messages">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{journal.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{journal.user.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="journal.notes|safeHtml"></div>
<div class="post-reply-date fl">{{journal.lasted_comment}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addHomeworkReply(formData)" value="回复" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<title>react js</title>
<meta charset='utf-8' />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta name="apple-mobile-web-app-capable" content="no">
<meta content='True' name='HandheldFriendly' />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link type="text/css" rel="stylesheet" href="/stylesheets/weui/weixin.css" />
</head>
<body>
<div id="container"></div>
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<script id="t:result-list" type="text/html">
<! for(var i =0; i < issues.length; ++i){ !>
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img src="<!=issues[i].author.img_url!>" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb"><!=issues[i].subject!></span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10"><!=issues[i].author.nickname!></a>项目问题</div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10">
<div class="post-all-content"><!=issues[i].description!></div>
</div>
<a herf="javascript:void(0);" class="link-blue f13 fl mt5 post-more " style="text-decoration:underline">点击展开</a>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl"><!=issues[i].created_on!></span>
<div class="cl"></div>
</div>
</div>
</div>
<! } !>
</script>
<script src="/javascripts/jquery-1.3.2.js"></script>
<script src="/javascripts/baiduTemplate.js"></script>
<script src="/javascripts/wechat/auth.js"></script>
<script src="/javascripts/wechat/wechat-dev.js"></script>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{issue.author.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{issue.subject}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{issue.author.realname}}</a>to<span class="ml10">{{issue.project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;项目缺陷</span></div>
<div class="cl"></div>
<div class="post-content" style="height:auto;">
<div class="post-all-content c-grey2 mt10" ng-bind-html="issue.description|safeHtml"></div>
<span class="mr15">状态:{{issue.issue_status}}</span> <span class="mr15">优先级:{{issue.issue_priority}}</span> <br />
<span class="mr15">指派给:{{issue.issue_assigned_to}}</span> <span class="mr15">完成度:{{issue.done_ratio}}%</span>
</div>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{issue.created_on}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{issue.journals_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="issue.has_praise" ng-click="decreasePraise(issue);">已赞 ({{issue.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!issue.has_praise" ng-click="addPraise(issue);">赞 ({{issue.praise_count}})</div>
</div>
<div id="all_issue_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="journal in issue.issue_journals">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{journal.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{journal.user.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="journal.notes|safeHtml"></div>
<div class="post-reply-date fl">{{journal.created_on}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addIssueReply(formData)" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,43 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{message.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title mb5 hidden"><a herf="javascript:void(0);" class="mr10">{{message.user.realname}}</a><span style="vertical-align:top;">给您留言了</span><br /></div>
<div class="post-title hidden">{{message.created_on}}</div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10" style="height:auto;">
<div class="post-all-content" ng-bind-html="message.notes|safeHtml"></div>
</div>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{message.reply_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="message.has_praise" ng-click="decreasePraise(message);">已赞 ({{message.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!message.has_praise" ng-click="addPraise(message);">赞 ({{message.praise_count}})</div>
</div>
<div id="all_message_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="journal in message.child_reply">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{journal.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{journal.user.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="journal.notes|safeHtml"></div>
<div class="post-reply-date fl">{{journal.lasted_comment}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addJournalReply(formData)" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,45 @@
<!-- 模板1开始可以使用scripttype设置为text/html来存放模板片段并且用id标示 -->
<div class="post-container">
<div class="post-wrapper">
<div class="post-main">
<div class="post-avatar fl"><img ng-src="{{discussion.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="post-title hidden mb5"><span class="c-grey3 f15 fb">{{discussion.subject}}</span></div>
<div class="post-title hidden"><a herf="javascript:void(0);" class="mr10">{{discussion.user.realname}}</a>to<a herf="javascript:void(0);" class="ml10">{{discussion.course_project_name}}&nbsp;&nbsp;|&nbsp;&nbsp;项目讨论区</a></div>
<div class="cl"></div>
<div class="post-content c-grey2 mt10" style="height:auto;">
<div class="post-all-content" ng-bind-html="discussion.content|safeHtml"></div>
</div>
<div class="cl"></div>
<span class="c-grey f13 mt10 fl">{{discussion.created_on}}</span>
<div class="cl"></div>
</div>
<div class="post-interactive border-bottom">
<div class="post-interactive-reply c-grey2">回复 <span class="reply-num">({{discussion.replies_count}})</span></div>
<div class="post-interactive-column c-grey2" ng-if="discussion.has_praise" ng-click="decreasePraise(discussion);">已赞 ({{discussion.praise_count}})</div>
<div class="post-interactive-column c-grey2" ng-if="!discussion.has_praise" ng-click="addPraise(discussion);">赞 ({{discussion.praise_count}})</div>
</div>
<div id="all_course_message_reply">
<div class="post-reply-wrap border-bottom" ng-repeat="journal in discussion.message_children">
<div class="post-reply-row">
<div class="post-reply-avatar fl"><img ng-src="{{journal.user.img_url}}" width="45" height="45" class="border-radius" /></div>
<div class="ml55">
<div class="post-reply-user hidden">{{journal.user.realname}}</div>
<div class="post-reply-content c-grey2 mb10" ng-bind-html="journal.content|safeHtml"></div>
<div class="post-reply-date fl">{{journal.lasted_comment}}</div>
<div class="post-reply-trigger fr undis">回复</div>
</div>
<div class="cl"></div>
</div>
</div>
</div>
<div class="post-input-wrap">
<div class="post-reply-row">
<!--<div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="30" height="30" /></div>-->
<input type="text" class="post-reply-input" id="postInput" ng-model="formData.comment" />
<button ng-click="addDiscussionReply(formData)" class="post-reply-submit fr mt10">回复</button>
<div class="cl"></div>
</div>
</div>
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,108 @@
/**
* Created by guange on 16/3/19.
*/
var CommentBox = React.createClass({
loadFromServer: function(){
$.ajax({
url: this.props.url,
dataType: 'json',
success: function(data){
this.setState({data: data});
}.bind(this),
error: function(xhr,status,err){
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
onCommentSubmit: function(comment){
console.log(comment);
},
getInitialState: function(){
return {data: []};
},
componentDidMount: function(){
this.loadFromServer();
setInterval(this.loadFromServer, 2000);
},
render: function(){
return(
<div className="commentBox">
<CommentForm onCommentSubmit={this.onCommentSubmit}/>
<CommentList data={this.state.data}/>
</div>
);
}
});
var CommentList = React.createClass({
render: function(){
var commentNodes = this.props.data.map(function(comment){
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
)
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentForm = React.createClass({
handleSubmit: function(e){
e.preventDefault();
var author = this.refs.author.value.trim();
var text = this.refs.text.value.trim();
if(!text || !author){
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.value = '';
this.refs.text.value = '';
return;
},
render: function(){
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your name" ref="author" />
<input type="text" placeholder="Say something..." ref="text" />
<input type="submit" value="Post" />
</form>
);
}
});
var Comment = React.createClass({
rawMarkup: function() {
var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
return { __html: rawMarkup };
},
render: function(){
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()}></span>
</div>
)
}
})
React.render(<CommentBox url="api/comment.json"/>, document.getElementById("example"));

View File

@ -0,0 +1,43 @@
$(function(){
window.byAlert = function(info, title){
if(typeof title === 'undefined'){
title = '提示';
}
$('.weui_dialog_alert .weui_dialog_title').text(title);
$('.weui_dialog_alert .weui_dialog_info').text(info);
var $dialog = $('#dialog2');
$dialog.show();
$dialog.find('.weui_btn_dialog').one('click', function () {
$dialog.hide();
});
};
window.byConfirm = function(info, cb){
var title;
if(typeof title === 'undefined'){
title = '提示';
}
$('.weui_dialog_confirm .weui_dialog_title').text(title);
$('.weui_dialog_confirm .weui_dialog_info').text(info);
var $dialog = $('#dialog1');
$dialog.show();
$dialog.find('.weui_btn_dialog.confirm').one('click', function () {
$dialog.hide();
if(typeof cb === 'function'){
cb();
}
});
$dialog.find('.weui_btn_dialog.cancel').one('click', function () {
$dialog.hide();
});
}
window.closeWindow = function(){
WeixinJSBridge.call('closeWindow');
}
});

View File

@ -0,0 +1,439 @@
var app = angular.module('wechat', ['ngRoute','ngCookies']);
var apiUrl = 'http://wechat.trustie.net/api/v1/';
var debug = false; //调试标志,如果在本地请置为true
if(debug===true){
apiUrl = 'http://localhost:3000/api/v1/';
}
app.factory('auth', function($http,$routeParams, $cookies, $q){
var _openid = '';
if(debug===true){
_openid = "3";
}
var getOpenId = function() {
var deferred = $q.defer();
if (typeof _openid !== 'undefined' && _openid.length > 0){
deferred.resolve(_openid);
} else {
var code = $routeParams.code;
$http({
url: '/wechat/get_open_id',
data: {code: code},
method: 'POST'
}).then(function successCallback(response) {
_openid = response.data.openid;
if(typeof _openid !== 'undefined' && _openid.length>0){
if(debug !== true){ //如果是生产环境,就存到cookies中
$cookies.put("openid", _openid);
}
} else {
if(debug!==true){//考虑从cookies中取出
_openid = $cookies.get('openid');
}
}
deferred.resolve(_openid);
}, function errorCallback(response) {
deferred.reject(response);
});
}
return deferred.promise;
};
var openid = function(){
return _openid;
};
return {getOpenId: getOpenId, openid: openid};
});
app.factory('rms', function(){
var _saveStorage = {};
var save = function(key, value){
_saveStorage[key] = value;
};
var get = function(key){
return _saveStorage[key];
};
return {save: save, get: get};
});
app.controller('ActivityController',function($scope, $http, auth, rms, common){
$scope.replaceUrl = function(url){
return "http://www.trustie.net/" + url;
};
console.log("ActivityController load");
$scope.activities = rms.get("activities") || [];
$scope.page = 0;
var loadActData = function(page){
$scope.page = page;
$http({
method: 'POST',
url: apiUrl+ "activities",
data: {openid: auth.openid(), page: page}
}).then(function successCallback(response) {
$scope.current_page = 0;
console.log($scope.current_page);
console.log(response.data.page);
if($scope.current_page < response.data.page) {
$scope.activities = $scope.activities.concat(response.data.data);
} else {
$scope.activities = response.data.data;
}
$scope.current_page = response.data.page;
$scope.all_count = response.data.all_count;
$scope.count = response.data.count;
console.log(response.data);
rms.save('activities', $scope.activities);
}, function errorCallback(response) {
});
};
auth.getOpenId().then(
function successCallback(response){
loadActData($scope.page);
}, function errorCallback(response) {
alert("获取openid出错:"+response);
}
);
$scope.loadActData = loadActData;
$scope.addPraise = function(act){
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
common.decreaseCommonPraise(act);
};
});
app.factory('common', function($http, auth, $routeParams){
var addCommonReply = function(id, type, data, cb){
if(!data.comment || data.comment.length<=0){
return;
}
var userInfo = {
type: type,
content: data.comment,
openid: auth.openid()
};
$http({
method: 'POST',
url: apiUrl+ "new_comment/"+id,
data: userInfo
}).then(function successCallback(response) {
alert("提交成功");
if(typeof cb === 'function'){
cb();
}
}, function errorCallback(response) {
});
};
var loadCommonData = function(id, type){
return $http({
method: 'GET',
url: apiUrl+ type + "/" + id+"?openid="+auth.openid()
})
};
var addCommonPraise = function(act){
act.praise_count += 1;
act.has_praise = true;
$http({
method: 'POST',
url: apiUrl + "praise/" + act.act_id,
data:{openid:auth.openid(),type:act.act_type}
}).then(function successCallback(response) {
console.log(response.data);
}, function errorCallback(response) {
});
};
var decreaseCommonPraise = function(act){
act.praise_count -= 1;
act.has_praise = false;
$http({
method: 'POST',
url: apiUrl + "praise/" + act.act_id,
data:{openid:auth.openid(),type:act.act_type}
}).then(function successCallback(response) {
console.log(response.data);
}, function errorCallback(response) {
});
};
return {addCommonReply: addCommonReply, loadCommonData: loadCommonData, addCommonPraise: addCommonPraise, decreaseCommonPraise: decreaseCommonPraise};
});
app.controller('IssueController', function($scope, $http, $routeParams, auth, common){
$scope.formData = {comment: ''};
var loadData = function(id){
common.loadCommonData(id, 'issues').then(function successCallback(response) {
console.log(response.data);
$scope.issue = response.data.data;
}, function errorCallback(response) {
});
};
loadData($routeParams.id);
$scope.addIssueReply = function(data){
console.log(data.comment);
common.addCommonReply($routeParams.id, 'Issue', data, function(){
$scope.formData = {comment: ''};
loadData($routeParams.id);
});
};
$scope.addPraise = function(act){
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
common.decreaseCommonPraise(act);
};
});
app.controller('HomeworkController', function($scope, $http, $routeParams, auth, common){
$scope.formData = {comment: ''};
var loadData = function(id){
common.loadCommonData(id, 'whomeworks').then(function successCallback(response) {
console.log(response.data);
$scope.homework = response.data.data;
}, function errorCallback(response) {
});
};
loadData($routeParams.id);
$scope.addHomeworkReply = function(data){
console.log(data.comment);
common.addCommonReply($routeParams.id, 'HomeworkCommon', data, function(){
$scope.formData = {comment: ''};
loadData($routeParams.id);
});
};
$scope.addPraise = function(act){
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
common.decreaseCommonPraise(act);
};
});
app.controller('CourseNoticeController', function($scope, $http, $routeParams, auth, common){
$scope.formData = {comment: ''};
var loadData = function(id){
common.loadCommonData(id, 'newss').then(function successCallback(response) {
console.log(response.data);
$scope.news = response.data.data;
}, function errorCallback(response) {
});
};
loadData($routeParams.id);
$scope.addNoticeReply = function(data){
console.log(data.comment);
common.addCommonReply($routeParams.id, 'News', data, function(){
$scope.formData = {comment: ''};
loadData($routeParams.id);
});
};
$scope.addPraise = function(act){
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
common.decreaseCommonPraise(act);
};
});
app.controller('DiscussionController', function($scope, $http, $routeParams, auth, common){
$scope.formData = {comment: ''};
var loadData = function(id){
common.loadCommonData(id, 'messages').then(function successCallback(response) {
console.log(response.data);
$scope.discussion = response.data.data;
}, function errorCallback(response) {
});
};
loadData($routeParams.id);
$scope.addDiscussionReply = function(data){
console.log(data.comment);
common.addCommonReply($routeParams.id, 'Message', data, function(){
$scope.formData = {comment: ''};
loadData($routeParams.id);
});
};
$scope.addPraise = function(act){
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
common.decreaseCommonPraise(act);
};
});
app.controller('JournalsController', function($scope, $http, $routeParams, auth, common){
$scope.formData = {comment: ''};
var loadData = function(id){
common.loadCommonData(id, 'journal_for_messages').then(function successCallback(response) {
console.log(response.data);
$scope.message = response.data.data;
}, function errorCallback(response) {
});
};
loadData($routeParams.id);
$scope.addJournalReply = function(data){
console.log(data.comment);
common.addCommonReply($routeParams.id, 'JournalsForMessage', data, function(){
$scope.formData = {comment: ''};
loadData($routeParams.id);
});
};
$scope.addPraise = function(act){
console.log(act);
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
console.log(act);
common.decreaseCommonPraise(act);
};
});
app.controller('BlogController', function($scope, $http, $routeParams, auth, common){
$scope.formData = {comment: ''};
var loadData = function(id){
common.loadCommonData(id, 'blog_comments').then(function successCallback(response) {
console.log(response.data);
$scope.blog = response.data.data;
}, function errorCallback(response) {
});
};
loadData($routeParams.id);
$scope.addBlogReply = function(data){
console.log(data.comment);
common.addCommonReply($routeParams.id, 'BlogComment', data, function(){
$scope.formData = {comment: ''};
loadData($routeParams.id);
});
};
$scope.addPraise = function(act){
console.log(act);
common.addCommonPraise(act);
};
$scope.decreasePraise = function(act){
console.log(act);
common.decreaseCommonPraise(act);
};
});
app.filter('safeHtml', function ($sce) {
return function (input) {
return $sce.trustAsHtml(input);
}
});
app.directive('textAutoHeight', function($timeout){
return {
restrict: 'A',
scope: {},
link: function(scope, element, attr){
scope.text = '点击展开';
$timeout(function(){
var e = element.parent().children().eq(4);
var height = e[0].scrollHeight;
var offsetHeight = e[0].offsetHeight;
if(height>90){
element.css('display', 'block');
element.on('click', function(){
if(element.text() == "点击展开"){
e.css("height", height+'px');
element.text("点击隐藏");
} else {
e.css("height", '90px');
element.text("点击展开");
}
});
}
}, false);
}
}
});
app.config(['$routeProvider',function ($routeProvider) {
$routeProvider
.when('/activities', {
templateUrl: 'activities.html',
controller: 'ActivityController'
})
.when('/issues/:id', {
templateUrl: 'issue_detail.html',
controller: 'IssueController'
})
.when('/project_discussion/:id', {
templateUrl: 'project_discussion.html',
controller: 'DiscussionController'
})
.when('/homework/:id', {
templateUrl: 'homework_detail.html',
controller: 'HomeworkController'
})
.when('/course_notice/:id', {
templateUrl: 'course_notice.html',
controller: 'CourseNoticeController'
})
.when('/course_discussion/:id', {
templateUrl: 'course_discussion.html',
controller: 'DiscussionController'
})
.when('/journal_for_message/:id', {
templateUrl: 'jour_message_detail.html',
controller: 'JournalsController'
})
.when('/blog_comment/:id', {
templateUrl: 'blog_detail.html',
controller: 'BlogController'
})
.otherwise({
redirectTo: '/activities'
});
}]);

View File

@ -0,0 +1,36 @@
$(function(){
//获取url中的参数
function getUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
var r = window.location.search.substr(1).match(reg); //匹配目标参数
if (r != null) return unescape(r[2]); return null; //返回参数值
}
var debug = false;
var g_openid = "";
if(debug){
g_openid = "填写要调试的openid即可";
}
window.getOpenId = function(cb){
if (g_openid.length>0){
cb(g_openid);
}
var code = getUrlParam("code");
$.ajax({
url: '/wechat/get_open_id',
data: {code: code},
type: 'post',
dataType: 'json',
success: function(data){
g_openid = data.openid;
cb(g_openid);
},
error: function(xhr,err){
alert("认证失败: "+err);
}
});
}
});

View File

@ -0,0 +1,95 @@
/**
* Created by root on 4/1/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setTemplate = function(data){
console.log(data);
var html=bt('t:blog-detail',{blog: data});
$('#blog-container').prepend(html);
$('.post-reply-submit').click(function(){
replyInsert();
});
$('post-interactive-praise').click(function(){
praiseClick();
});
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'blog_comments/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var homeworkUrl = window.location.search;
var homeworkID = homeworkUrl.split("=")[1];
loadDataFromServer(homeworkID);
//点击回复按钮,插入回复内容
var replyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
//将用户输入内容插入最后一条回复
$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis">回复</div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
//获取并传送回复用户数据
var userInfo = {
"replyType" : "homework_assignment",
"replyContent" : postInput
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: "前台地址/后台方法", //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
alert(data.d); //用data.d来获取后台传过来的json语句或者是单纯的语句
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
});
}
}
//点赞效果
var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
}
});

View File

@ -0,0 +1,102 @@
/**
* Created by root on 4/1/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setReplyTemplate = function(data){
console.log(data);
var html=bt('t:c-message-detail-reply',{reply: data});
$('#all_course_message_reply').prepend(html);
};
var setTemplate = function(data){
console.log(data);
var html=bt('t:course-discussion',{discussion: data});
$('#c-discussion-container').prepend(html);
$('.post-reply-submit').click(function(){
replyInsert();
});
/*$('post-interactive-praise').click(function(){
praiseClick();
});*/
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'messages/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var homeworkUrl = window.location.search;
var homeworkID = homeworkUrl.split("=")[1];
loadDataFromServer(homeworkID);
//点击回复按钮,插入回复内容
var replyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
//将用户输入内容插入最后一条回复
/*$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis"></div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());*/
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
//获取并传送回复用户数据
var userInfo = {
"Type" : "Message",
"Content" : postInput
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: apiUrl + 'new_comment/' + homeworkID, //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
setReplyTemplate(data.data);
alert("6");
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
});
}
}
/*//点赞效果
var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
}*/
});

View File

@ -0,0 +1,101 @@
/**
* Created by root on 4/1/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setReplyTemplate = function(data){
console.log(data);
var html=bt('t:news-detail-reply',{reply: data});
$('#all_news_reply').prepend(html);
};
var setTemplate = function(data){
console.log(data);
var html=bt('t:course-notice',{course: data});
$('#c-notice-container').prepend(html);
$('.post-reply-submit').click(function(){
replyInsert();
});
/*$('post-interactive-praise').click(function(){
praiseClick();
});*/
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'newss/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var homeworkUrl = window.location.search;
var homeworkID = homeworkUrl.split("=")[1];
loadDataFromServer(homeworkID);
//点击回复按钮,插入回复内容
var replyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
//将用户输入内容插入最后一条回复
/*$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis"></div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());*/
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
//获取并传送回复用户数据
var userInfo = {
"type" : "News",
"content" : postInput
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: apiUrl + 'new_comment/' + homeworkID, //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
setReplyTemplate(data.data);
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
});
}
}
//点赞效果
var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
}
});

View File

@ -0,0 +1,104 @@
/**
* Created by root on 3/31/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setReplyTemplate = function(data){
console.log(data);
var html=bt('t:homework-detail-reply',{reply: data});
$('#all_homework_reply').prepend(html);
};
var setTemplate = function(data){
console.log(data);
var html=bt('t:homework-detail',{homework: data});
$('#homework-container').prepend(html);
$('.post-reply-submit').click(function(){
replyInsert();
});
/*$('post-interactive-praise').click(function(){
praiseClick();
});*/
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'whomeworks/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var homeworkUrl = window.location.search;
var homeworkID = homeworkUrl.split("=")[1];
loadDataFromServer(homeworkID);
//点击回复按钮,插入回复内容
var replyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
/*//将用户输入内容插入最后一条回复
$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis">回复</div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());*/
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
getOpenId(function(openid) {
//获取并传送回复用户数据
var userInfo = {
"type": "HomeworkCommon",
"content": postInput,
openid: openid
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: apiUrl + 'new_comment/' + homeworkID, //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
setReplyTemplate(data.data);
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
})
});
}
};
//点赞效果
/*var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
};*/
});

View File

@ -0,0 +1,108 @@
/**
* Created by root on 4/1/16.
*/
/**
* Created by root on 3/31/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setReplyTemplate = function(data){
console.log(data);
var html=bt('t:issue-detail-reply',{issue_reply: data});
$('#all_issue_reply').prepend(html);
};
var setTemplate = function(data){
console.log(data);
var html=bt('t:issue-detail',{issues: data});
$('#issue-container').prepend(html);
$('.post-reply-submit').click(function(){
IssueReplyInsert();
});
/*$('post-interactive-praise').click(function(){
praiseClick();
});*/
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'issues/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var IssueUrl = window.location.search;
var IssueID = IssueUrl.split("=")[1];
loadDataFromServer(IssueID);
//点击回复按钮,插入回复内容
var IssueReplyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
//将用户输入内容插入最后一条回复
/*$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis"></div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());*/
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
getOpenId(function(openid) {
//获取并传送回复用户数据
var userInfo = {
"type": "Issue",
"content": postInput,
openid: openid,
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: apiUrl + 'new_comment/' + IssueID, //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
setReplyTemplate(data.data);
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
})
});
}
};
//点赞效果
/*var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
};*/
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,95 @@
/**
* Created by root on 4/1/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setTemplate = function(data){
console.log(data);
var html=bt('t:message-detail',{message: data});
$('#message-container').prepend(html);
$('.post-reply-submit').click(function(){
replyInsert();
});
$('post-interactive-praise').click(function(){
praiseClick();
});
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'journal_for_messages/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var homeworkUrl = window.location.search;
var homeworkID = homeworkUrl.split("=")[1];
loadDataFromServer(homeworkID);
//点击回复按钮,插入回复内容
var replyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
//将用户输入内容插入最后一条回复
$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis">回复</div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
//获取并传送回复用户数据
var userInfo = {
"replyType" : "homework_assignment",
"replyContent" : postInput
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: "前台地址/后台方法", //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
alert(data.d); //用data.d来获取后台传过来的json语句或者是单纯的语句
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
});
}
}
//点赞效果
var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
}
});

View File

@ -0,0 +1,105 @@
/**
* Created by root on 4/1/16.
*/
/**
* Created by root on 4/1/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var setReplyTemplate = function(data){
console.log(data);
var html=bt('t:homework-detail-reply',{reply: data});
$('#all_homework_reply').prepend(html);
};
var setTemplate = function(data){
console.log(data);
var html=bt('t:project-discussion',{discussion: data});
$('#p-discussion-container').prepend(html);
$('.post-reply-submit').click(function(){
replyInsert();
});
/*$('post-interactive-praise').click(function(){
praiseClick();
});*/
};
var loadDataFromServer = function(id){
//getOpenId(function(openid){
$.ajax({
url: apiUrl + 'messages/' + id,
dataType: 'json',
success: function(data){
setTemplate(data.data);
},
error: function(xhr,status,err){
console.log(err);
}
});
//})
};
var homeworkUrl = window.location.search;
var homeworkID = homeworkUrl.split("=")[1];
loadDataFromServer(homeworkID);
//点击回复按钮,插入回复内容
var replyInsert = function(){
var replyContent = $("#postInput").val();
if (!replyContent){
alert("请输入回复");
}else{
/*//将用户输入内容插入最后一条回复
$(".post-reply-wrap:last").after('<div class="post-reply-wrap border-bottom"><div class="post-reply-row"><div class="post-reply-avatar fl"><img src="images/post-avatar.jpg" width="45" height="45" /></div><div class="ml55"><div class="post-reply-user hidden">Mrs. Ashford</div><div class="post-reply-content c-grey2 mb10"></div><div class="post-reply-date fl"></div><div class="post-reply-trigger fr undis">回复</div></div><div class="cl"></div></div> </div>');
$(".post-reply-content:last").append(replyContent);
$(".post-reply-date:last").append(Date());*/
var postInput = $("#postInput").val();
$("#postInput").val("");
//回复数目+1
var replyNum = $(".post-interactive-reply").text().match(/\d+/g);
replyNum++;
$(".reply-num").text("(" + replyNum + ")");
//获取并传送回复用户数据
var userInfo = {
"type" : "Message",
"content" : postInput
};
$.ajax({
type: "POST", //提交方式
dataType: "json", //类型
url: apiUrl + 'new_comment/' + homeworkID, //提交的页面,方法名
data: userInfo, //参数如果没有可以为null
success: function (data) { //如果执行成功,那么执行此方法
setReplyTemplate(data.data); //用data.d来获取后台传过来的json语句或者是单纯的语句
},
error: function (err) { //如果执行不成功,那么执行此方法
alert("err:" + err);
}
});
}
}
//点赞效果
/*var praiseClick = function(){
var praiseNum = $(".post-interactive-praise").text().match(/\d+/g);
praiseNum++;
$(".praise-num").text("(" + praiseNum + ")");
}*/
});

View File

@ -0,0 +1,57 @@
/**
* Created by root on 3/25/16.
*/
$(document).ready(function(){
var bt=baidu.template;
bt.LEFT_DELIMITER='<!';
bt.RIGHT_DELIMITER='!>';
var apiUrl = '/api/v1/';
var loadDataFromServer = function(id, page){
getOpenId(function(openid){
$.ajax({
url: apiUrl + 'activities',
data: {openid: openid, page: page},
type: 'POST',
dataType: 'json',
success: function(data){
setTemplate(data.data, data.all_count, data.count, data.page);
},
error: function(xhr,status,err){
console.log(err);
}
});
})
};
var setTemplate = function(data, all_count, count, page){
console.log(data);
var html=bt('t:result-list',{activities: data, all_count: all_count, count: count, page: page});
if (page == 0) {
$('#container').prepend(html);
} else {
$("#more_activities").remove();
$('#container').append(html);
}
descToggle();
};
//内容全部显示与部分隐藏
var descToggle = function(){
$(".post-all-content").each(function(){
var postHeight = $(this).height();
if (postHeight > 90){
$(this).parent().next().css("display","block");
$(this).parent().next().toggle(function(){
$(this).text("点击隐藏");
$(this).prev().css("height",postHeight);
},function(){
$(this).text("点击展开");
$(this).prev().css("height",90);
});
}
});
}
loadDataFromServer(8686, 0);
});

View File

@ -1,354 +1,354 @@
/*
# Code Review plugin for Redmine
# Copyright (C) 2009-2013 Haruyuki Iida
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
var topZindex = 1000;
var action_type = '';
var rev = '';
var rev_to = '';
var path = '';
var urlprefix = '';
var review_form_dialog = null;
var add_form_title = null;
var review_dialog_title = null;
var repository_id = null;
var filenames = [];
var ReviewCount = function(total, open, progress){
this.total = total;
this.open = open;
this.closed = total - open;
this.progress = progress
};
var CodeReview = function(id) {
this.id = id;
this.path = '';
this.line = 0;
this.url = '';
this.is_closed = false;
};
var review_counts = new Array();
var code_reviews_map = new Array();
var code_reviews_dialog_map = new Array();
function UpdateRepositoryView(title) {
var header = $("table.changesets thead tr:first");
var th = $('<th></th>');
th.html(title);
header.append(th);
$('tr.changeset td.id a').each(function(i){
var revision = this.getAttribute("href");
revision = revision.substr(revision.lastIndexOf("/") + 1);
var review = review_counts['revision_' + revision];
var td = $('<td/>',{
'class':'progress'
});
td.html(review.progress);
$(this.parentNode.parentNode).append(td);
});
}
//add function $.down
if(! $.fn.down)
(function($) {
$.fn.down = function() {
var el = this[0] && this[0].firstChild;
while (el && el.nodeType != 1)
el = el.nextSibling;
return $(el);
};
})(jQuery);
function UpdateRevisionView() {
$('li.change').each(function(){
var li = $(this);
if (li.hasClass('folder')) return;
var a = li.down('a');
if (a.size() == 0) return;
var path = a.attr('href').replace(urlprefix, '').replace(/\?.*$/, '');
var reviewlist = code_reviews_map[path];
if (reviewlist == null) return;
var ul = $('<ul></ul>');
for (var j = 0; j < reviewlist.length; j++) {
var review = reviewlist[j];
var icon = review.is_closed? 'icon-closed-review': 'icon-review';
var item = $('<li></li>', {
'class': 'icon ' + icon + ' code_review_summary'
});
item.html(review.url);
ul.append(item);
}
li.append(ul);
});
}
function setAddReviewButton(url, change_id, image_tag, is_readonly, is_diff, attachment_id){
var filetables = [];
var j = 0;
$('table').each(function(){
if($(this).hasClass('filecontent')){
filetables[j++] = this;
}
});
j = 0;
$('table.filecontent th.filename').each(function(){
filenames[j] = $.trim($(this).text());
j++;
});
addReviewUrl = url + '?change_id=' + change_id + '&action_type=' + action_type +
'&rev=' + rev + '&rev_to=' + rev_to +
'&attachment_id=' + attachment_id + '&repository_id=' + encodeURIComponent(repository_id);
if (path != null && path.length > 0) {
addReviewUrl = addReviewUrl + '&path=' + encodeURIComponent(path);
}
var num = 0;
if (is_diff) {
num = 1;
}
var i, l, tl;
for (i = 0, tl = filetables.length; i < tl; i++) {
var table = filetables[i];
var trs = table.getElementsByTagName('tr');
for (j = 0,l = trs.length; j < l; j++) {
var tr = trs[j];
var ths = tr.getElementsByTagName('th');
var th = ths[num];
if (th == null) {
continue;
}
var th_html = th.innerHTML;
var line = th_html.match(/[0-9]+/);
if (line == null) {
continue;
}
var span_html = '<span white-space="nowrap" id="review_span_' + line + '_' + i + '">';
if (!is_readonly) {
span_html += image_tag;
}
span_html += '</span>';
th.innerHTML = th_html + span_html;
var img = th.getElementsByTagName('img')[0];
if (img != null ) {
img.id = 'add_revew_img_' + line + '_' + i;
$(img).click(clickPencil);
}
}
}
}
function clickPencil(e)
{
// alert('$(e.target).attr("id") = ' + $(e.target).attr("id"));
var result = $(e.target).attr("id").match(/([0-9]+)_([0-9]+)/);
var line = result[1];
var file_count = eval(result[2]);
var url = addReviewUrl + '&line=' + line + '&file_count=' + file_count;
if (path == null || path.length == 0) {
url = url + '&path=' + encodeURIComponent(filenames[file_count]) + '&diff_all=true';
}
addReview(url);
formPopup(e.pageX, e.pageY);
e.preventDefault();
}
var addReviewUrl = null;
var showReviewUrl = null;
var showReviewImageTag = null;
var showClosedReviewImageTag = null;
function setShowReviewButton(line, review_id, is_closed, file_count) {
//alert('file_count = ' + file_count);
var span = $('#review_span_' + line + '_' + file_count);
if (span.size() == 0) {
return;
}
var innerSpan = $('<span></span>',{id: 'review_' + review_id});
span.append(innerSpan);
innerSpan.html(is_closed? showClosedReviewImageTag : showReviewImageTag);
var div = $('<div></div>', {
'class':'draggable',
id: 'show_review_' + review_id
});
$('#code_review').append(div);
innerSpan.down('img').click(function(e) {
var review_id = $(e.target).parent().attr('id').match(/[0-9]+/)[0];
var span = $('#review_' + review_id); // span element of view review button
var pos = span.offset();
showReview(showReviewUrl, review_id, pos.left + 10 + 5, pos.top + 25);
});
}
function popupReview(review_id) {
var span = $('#review_' + review_id); // span element of view review button
var pos = span.offset();
$('html,body').animate({ scrollTop: pos.top },
{duration: 'fast',
complete: function(){showReview(showReviewUrl, review_id, pos.left + 10 + 5, pos.top)}});
// position and show popup dialog
// create popup dialog
//var win = showReview(showReviewUrl, review_id, pos.left + 10 + 5, pos.top);
// win.toFront();
}
function showReview(url, review_id, x, y) {
if (code_reviews_dialog_map[review_id] != null) {
var cur_win = code_reviews_dialog_map[review_id];
cur_win.hide();
code_reviews_dialog_map[review_id] = null;
}
$('#show_review_' + review_id).load(url, {review_id: review_id});
var review = getReviewObjById(review_id);
var win = $('#show_review_' + review_id).dialog({
show: {effect:'scale'},// ? 'top-left'
//position: [x, y + 5],
width:640,
zIndex: topZindex,
title: review_dialog_title
});
// win.getContent().style.color = "#484848";
// win.getContent().style.background = "#ffffff";
topZindex++;
code_reviews_dialog_map[review_id] = win;
return win
}
function getReviewObjById(review_id) {
for (var reviewlist in code_reviews_map) {
for (var i = 0; i < reviewlist.length; i++) {
var review = reviewlist[i];
if (review.id == review_id) {
return review;
}
}
}
return null;
}
function formPopup(x, y){
//@see http://docs.jquery.com/UI/Effects/Scale
var win = $('#review-form-frame').dialog({
show: {effect:'scale', direction: 'both'},// ? 'top-left'
// position: [x, y + 5],
width:640,
zIndex: topZindex,
title: add_form_title
});
// win.getContent().style.background = "#ffffff";
if (review_form_dialog != null) {
review_form_dialog.destroy();
review_form_dialog = null;
}
review_form_dialog = win;
topZindex += 10;
return false;
}
function hideForm() {
if (review_form_dialog == null) {
return;
}
review_form_dialog.dialog('close');
review_form_dialog = null;
$('#review-form').html('');
}
function addReview(url) {
$('#review-form').load(url);
}
function deleteReview(review_id) {
$('show_review_' + review_id).remove();
$('review_' + review_id).remove();
}
function changeImage(review_id, is_closed) {
var span = $('review_' + review_id);
var new_image = null;
var dummy = new Element('span');
if (is_closed) {
dummy.insert(showClosedReviewImageTag);
}
else {
dummy.insert(showReviewImageTag);
}
new_image = dummy.down().getAttribute('src');
//alert(new_image);
span.down('img').setAttribute('src', new_image);
}
function make_addreview_link(project, link) {
var alist = $('#content p a');
if (alist == null) {
return;
}
var a = alist[0];
var p = a.parentNode;
p.innerHTML = p.innerHTML + " | " + link;
}
function call_update_revisions(url) {
var changeset_ids = '';
var links = $$('table.changesets tbody tr.changeset td.id a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var href = link.getAttribute('href');
var id = href.replace(/^.*\/revisions\//, '');
if (i > 0) {
changeset_ids += ',';
}
changeset_ids += id;
}
new Ajax.Updater('code_review_revisions', url,
{
evalScripts:true,
method:'get',
parameters: 'changeset_ids=' + encodeURI(changeset_ids)
});
}
$.fn.serialize2json = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
/*
# Code Review plugin for Redmine
# Copyright (C) 2009-2013 Haruyuki Iida
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
var topZindex = 1000;
var action_type = '';
var rev = '';
var rev_to = '';
var path = '';
var urlprefix = '';
var review_form_dialog = null;
var add_form_title = null;
var review_dialog_title = null;
var repository_id = null;
var filenames = [];
var ReviewCount = function(total, open, progress){
this.total = total;
this.open = open;
this.closed = total - open;
this.progress = progress
};
var CodeReview = function(id) {
this.id = id;
this.path = '';
this.line = 0;
this.url = '';
this.is_closed = false;
};
var review_counts = new Array();
var code_reviews_map = new Array();
var code_reviews_dialog_map = new Array();
function UpdateRepositoryView(title) {
var header = $("table.changesets thead tr:first");
var th = $('<th></th>');
th.html(title);
header.append(th);
$('tr.changeset td.id a').each(function(i){
var revision = this.getAttribute("href");
revision = revision.substr(revision.lastIndexOf("/") + 1);
var review = review_counts['revision_' + revision];
var td = $('<td/>',{
'class':'progress'
});
td.html(review.progress);
$(this.parentNode.parentNode).append(td);
});
}
//add function $.down
if(! $.fn.down)
(function($) {
$.fn.down = function() {
var el = this[0] && this[0].firstChild;
while (el && el.nodeType != 1)
el = el.nextSibling;
return $(el);
};
})(jQuery);
function UpdateRevisionView() {
$('li.change').each(function(){
var li = $(this);
if (li.hasClass('folder')) return;
var a = li.down('a');
if (a.size() == 0) return;
var path = a.attr('href').replace(urlprefix, '').replace(/\?.*$/, '');
var reviewlist = code_reviews_map[path];
if (reviewlist == null) return;
var ul = $('<ul></ul>');
for (var j = 0; j < reviewlist.length; j++) {
var review = reviewlist[j];
var icon = review.is_closed? 'icon-closed-review': 'icon-review';
var item = $('<li></li>', {
'class': 'icon ' + icon + ' code_review_summary'
});
item.html(review.url);
ul.append(item);
}
li.append(ul);
});
}
function setAddReviewButton(url, change_id, image_tag, is_readonly, is_diff, attachment_id){
var filetables = [];
var j = 0;
$('table').each(function(){
if($(this).hasClass('filecontent')){
filetables[j++] = this;
}
});
j = 0;
$('table.filecontent th.filename').each(function(){
filenames[j] = $.trim($(this).text());
j++;
});
addReviewUrl = url + '?change_id=' + change_id + '&action_type=' + action_type +
'&rev=' + rev + '&rev_to=' + rev_to +
'&attachment_id=' + attachment_id + '&repository_id=' + encodeURIComponent(repository_id);
if (path != null && path.length > 0) {
addReviewUrl = addReviewUrl + '&path=' + encodeURIComponent(path);
}
var num = 0;
if (is_diff) {
num = 1;
}
var i, l, tl;
for (i = 0, tl = filetables.length; i < tl; i++) {
var table = filetables[i];
var trs = table.getElementsByTagName('tr');
for (j = 0,l = trs.length; j < l; j++) {
var tr = trs[j];
var ths = tr.getElementsByTagName('th');
var th = ths[num];
if (th == null) {
continue;
}
var th_html = th.innerHTML;
var line = th_html.match(/[0-9]+/);
if (line == null) {
continue;
}
var span_html = '<span white-space="nowrap" id="review_span_' + line + '_' + i + '">';
if (!is_readonly) {
span_html += image_tag;
}
span_html += '</span>';
th.innerHTML = th_html + span_html;
var img = th.getElementsByTagName('img')[0];
if (img != null ) {
img.id = 'add_revew_img_' + line + '_' + i;
$(img).click(clickPencil);
}
}
}
}
function clickPencil(e)
{
// alert('$(e.target).attr("id") = ' + $(e.target).attr("id"));
var result = $(e.target).attr("id").match(/([0-9]+)_([0-9]+)/);
var line = result[1];
var file_count = eval(result[2]);
var url = addReviewUrl + '&line=' + line + '&file_count=' + file_count;
if (path == null || path.length == 0) {
url = url + '&path=' + encodeURIComponent(filenames[file_count]) + '&diff_all=true';
}
addReview(url);
formPopup(e.pageX, e.pageY);
e.preventDefault();
}
var addReviewUrl = null;
var showReviewUrl = null;
var showReviewImageTag = null;
var showClosedReviewImageTag = null;
function setShowReviewButton(line, review_id, is_closed, file_count) {
//alert('file_count = ' + file_count);
var span = $('#review_span_' + line + '_' + file_count);
if (span.size() == 0) {
return;
}
var innerSpan = $('<span></span>',{id: 'review_' + review_id});
span.append(innerSpan);
innerSpan.html(is_closed? showClosedReviewImageTag : showReviewImageTag);
var div = $('<div></div>', {
'class':'draggable',
id: 'show_review_' + review_id
});
$('#code_review').append(div);
innerSpan.down('img').click(function(e) {
var review_id = $(e.target).parent().attr('id').match(/[0-9]+/)[0];
var span = $('#review_' + review_id); // span element of view review button
var pos = span.offset();
showReview(showReviewUrl, review_id, pos.left + 10 + 5, pos.top + 25);
});
}
function popupReview(review_id) {
var span = $('#review_' + review_id); // span element of view review button
var pos = span.offset();
$('html,body').animate({ scrollTop: pos.top },
{duration: 'fast',
complete: function(){showReview(showReviewUrl, review_id, pos.left + 10 + 5, pos.top)}});
// position and show popup dialog
// create popup dialog
//var win = showReview(showReviewUrl, review_id, pos.left + 10 + 5, pos.top);
// win.toFront();
}
function showReview(url, review_id, x, y) {
if (code_reviews_dialog_map[review_id] != null) {
var cur_win = code_reviews_dialog_map[review_id];
cur_win.hide();
code_reviews_dialog_map[review_id] = null;
}
$('#show_review_' + review_id).load(url, {review_id: review_id});
var review = getReviewObjById(review_id);
var win = $('#show_review_' + review_id).dialog({
show: {effect:'scale'},// ? 'top-left'
//position: [x, y + 5],
width:640,
zIndex: topZindex,
title: review_dialog_title
});
// win.getContent().style.color = "#484848";
// win.getContent().style.background = "#ffffff";
topZindex++;
code_reviews_dialog_map[review_id] = win;
return win
}
function getReviewObjById(review_id) {
for (var reviewlist in code_reviews_map) {
for (var i = 0; i < reviewlist.length; i++) {
var review = reviewlist[i];
if (review.id == review_id) {
return review;
}
}
}
return null;
}
function formPopup(x, y){
//@see http://docs.jquery.com/UI/Effects/Scale
var win = $('#review-form-frame').dialog({
show: {effect:'scale', direction: 'both'},// ? 'top-left'
// position: [x, y + 5],
width:640,
zIndex: topZindex,
title: add_form_title
});
// win.getContent().style.background = "#ffffff";
if (review_form_dialog != null) {
review_form_dialog.destroy();
review_form_dialog = null;
}
review_form_dialog = win;
topZindex += 10;
return false;
}
function hideForm() {
if (review_form_dialog == null) {
return;
}
review_form_dialog.dialog('close');
review_form_dialog = null;
$('#review-form').html('');
}
function addReview(url) {
$('#review-form').load(url);
}
function deleteReview(review_id) {
$('show_review_' + review_id).remove();
$('review_' + review_id).remove();
}
function changeImage(review_id, is_closed) {
var span = $('review_' + review_id);
var new_image = null;
var dummy = new Element('span');
if (is_closed) {
dummy.insert(showClosedReviewImageTag);
}
else {
dummy.insert(showReviewImageTag);
}
new_image = dummy.down().getAttribute('src');
//alert(new_image);
span.down('img').setAttribute('src', new_image);
}
function make_addreview_link(project, link) {
var alist = $('#content p a');
if (alist == null) {
return;
}
var a = alist[0];
var p = a.parentNode;
p.innerHTML = p.innerHTML + " | " + link;
}
function call_update_revisions(url) {
var changeset_ids = '';
var links = $$('table.changesets tbody tr.changeset td.id a');
for (var i = 0; i < links.length; i++) {
var link = links[i];
var href = link.getAttribute('href');
var id = href.replace(/^.*\/revisions\//, '');
if (i > 0) {
changeset_ids += ',';
}
changeset_ids += id;
}
new Ajax.Updater('code_review_revisions', url,
{
evalScripts:true,
method:'get',
parameters: 'changeset_ids=' + encodeURI(changeset_ids)
});
}
$.fn.serialize2json = function()
{
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};

View File

@ -1,4 +1,4 @@
dt.code_review {
background-image: url(../images/review.png);
dt.code_review {
background-image: url(../images/review.png);
}

View File

@ -1,97 +1,97 @@
/*
# Code Review plugin for Redmine
# Copyright (C) 2009 Haruyuki Iida
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#review-form-frame {
height: 100%;
}
.autoscroll table.filecontent th.line-num {
white-space: nowrap;
vertical-align: bottom;
padding-top: 0;
padding-bottom: 0;
text-align:left;
}
table.filecontent th.line-num img{
padding: 0;
margin: 0;
cursor: pointer;
}
.code-review-form-title {
background-color: #002059;
color: white;
padding-left: 2px;
padding-right: 2px;
cursor: default;
}
.code_review_viewer {
min-width: 300px;
/*
max-width: 60%;
*/
/* max-height: 400px; */
}
.code_review_viewer .issue{
}
.code_review_body {
background-color: white;
padding:2px;
}
#code_review_list table.list td {
text-align: center;
}
#code_review_list table.list td.path {
text-align: left;
}
#code_review_list table.list td.subject {
text-align: left;
}
.icon-review {
background-image: url(../images/review.png);
background-repeat: no-repeat;
}
.icon-closed-review {
background-image: url(../images/closed_review.png);
background-repeat: no-repeat;
}
.icon-settings {
background-image: url(../../../images/changeset.png);
background-repeat: no-repeat;
}
li.code_review_summary {
list-style-type: none;
/*
# Code Review plugin for Redmine
# Copyright (C) 2009 Haruyuki Iida
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#review-form-frame {
height: 100%;
}
.autoscroll table.filecontent th.line-num {
white-space: nowrap;
vertical-align: bottom;
padding-top: 0;
padding-bottom: 0;
text-align:left;
}
table.filecontent th.line-num img{
padding: 0;
margin: 0;
cursor: pointer;
}
.code-review-form-title {
background-color: #002059;
color: white;
padding-left: 2px;
padding-right: 2px;
cursor: default;
}
.code_review_viewer {
min-width: 300px;
/*
max-width: 60%;
*/
/* max-height: 400px; */
}
.code_review_viewer .issue{
}
.code_review_body {
background-color: white;
padding:2px;
}
#code_review_list table.list td {
text-align: center;
}
#code_review_list table.list td.path {
text-align: left;
}
#code_review_list table.list td.subject {
text-align: left;
}
.icon-review {
background-image: url(../images/review.png);
background-repeat: no-repeat;
}
.icon-closed-review {
background-image: url(../images/closed_review.png);
background-repeat: no-repeat;
}
.icon-settings {
background-image: url(../../../images/changeset.png);
background-repeat: no-repeat;
}
li.code_review_summary {
list-style-type: none;
}

View File

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

View File

@ -1,119 +1,119 @@
.overlay_alert {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.alert_nw {
width: 5px;
height: 5px;
background: transparent url(alert/top_left.gif) no-repeat bottom left;
}
.alert_n {
height: 5px;
background: transparent url(alert/top.gif) repeat-x bottom left;
}
.alert_ne {
width: 5px;
height: 5px;
background: transparent url(alert/top_right.gif) no-repeat bottom left
}
.alert_e {
width: 5px;
background: transparent url(alert/right.gif) repeat-y 0 0;
}
.alert_w {
width: 5px;
background: transparent url(alert/left.gif) repeat-y 0 0;
}
.alert_sw {
width: 5px;
height: 5px;
background: transparent url(alert/bottom_left.gif) no-repeat 0 0;
}
.alert_s {
height: 5px;
background: transparent url(alert/bottom.gif) repeat-x 0 0;
}
.alert_se, .alert_sizer {
width: 5px;
height: 5px;
background: transparent url(alert/bottom_right.gif) no-repeat 0 0;
}
.alert_close {
width:0px;
height:0px;
display:none;
}
.alert_minimize {
width:0px;
height:0px;
display:none;
}
.alert_maximize {
width:0px;
height:0px;
display:none;
}
.alert_title {
float:left;
height:1px;
width:100%;
}
.alert_content {
overflow:visible;
color: #000;
font-family: Tahoma, Arial, sans-serif;
font: 12px arial;
background: #FFF;
}
/* For alert/confirm dialog */
.alert_window {
background: #FFF;
padding:20px;
margin-left:auto;
margin-right:auto;
width:400px;
}
.alert_message {
font: 12px arial;
width:100%;
color:#F00;
padding-bottom:10px;
}
.alert_buttons {
text-align:center;
width:100%;
}
.alert_buttons input {
width:20%;
margin:10px;
}
.alert_progress {
float:left;
margin:auto;
text-align:center;
width:100%;
height:16px;
background: #FFF url('alert/progress.gif') no-repeat center center
}
.overlay_alert {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.alert_nw {
width: 5px;
height: 5px;
background: transparent url(alert/top_left.gif) no-repeat bottom left;
}
.alert_n {
height: 5px;
background: transparent url(alert/top.gif) repeat-x bottom left;
}
.alert_ne {
width: 5px;
height: 5px;
background: transparent url(alert/top_right.gif) no-repeat bottom left
}
.alert_e {
width: 5px;
background: transparent url(alert/right.gif) repeat-y 0 0;
}
.alert_w {
width: 5px;
background: transparent url(alert/left.gif) repeat-y 0 0;
}
.alert_sw {
width: 5px;
height: 5px;
background: transparent url(alert/bottom_left.gif) no-repeat 0 0;
}
.alert_s {
height: 5px;
background: transparent url(alert/bottom.gif) repeat-x 0 0;
}
.alert_se, .alert_sizer {
width: 5px;
height: 5px;
background: transparent url(alert/bottom_right.gif) no-repeat 0 0;
}
.alert_close {
width:0px;
height:0px;
display:none;
}
.alert_minimize {
width:0px;
height:0px;
display:none;
}
.alert_maximize {
width:0px;
height:0px;
display:none;
}
.alert_title {
float:left;
height:1px;
width:100%;
}
.alert_content {
overflow:visible;
color: #000;
font-family: Tahoma, Arial, sans-serif;
font: 12px arial;
background: #FFF;
}
/* For alert/confirm dialog */
.alert_window {
background: #FFF;
padding:20px;
margin-left:auto;
margin-right:auto;
width:400px;
}
.alert_message {
font: 12px arial;
width:100%;
color:#F00;
padding-bottom:10px;
}
.alert_buttons {
text-align:center;
width:100%;
}
.alert_buttons input {
width:20%;
margin:10px;
}
.alert_progress {
float:left;
margin:auto;
text-align:center;
width:100%;
height:16px;
background: #FFF url('alert/progress.gif') no-repeat center center
}

View File

@ -1,88 +1,88 @@
.overlay_alert_lite {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.alert_lite_sizer {
width:0px;
height:0px;
display:none;
}
.alert_lite_close {
width:0px;
height:0px;
display:none;
}
.alert_lite_minimize {
width:0px;
height:0px;
display:none;
}
.alert_lite_maximize {
width:0px;
height:0px;
display:none;
}
.alert_lite_title {
width:0px;
height:0px;
display:none;
}
.alert_lite_content {
overflow:auto;
color: #000;
font-family: Tahoma, Arial, sans-serif;
font-size: 10px;
background: #FFF;
}
/* For alert/confirm dialog */
.alert_lite_window {
border:1px solid #F00;
background: #FFF;
padding:20px;
margin-left:auto;
margin-right:auto;
width:400px;
}
.alert_lite_message {
font-size:16px;
text-align:center;
width:100%;
color:#F00;
padding-bottom:10px;
}
.alert_lite_buttons {
text-align:center;
width:100%;
}
.alert_lite_buttons input {
width:20%;
margin:10px;
}
.alert_lite_progress {
float:left;
margin:auto;
text-align:center;
width:100%;
height:16px;
background: #FFF url('alert/progress.gif') no-repeat center center
}
table.alert_lite_header {
border:1px solid #F00;
background:#FFF
}
.overlay_alert_lite {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.alert_lite_sizer {
width:0px;
height:0px;
display:none;
}
.alert_lite_close {
width:0px;
height:0px;
display:none;
}
.alert_lite_minimize {
width:0px;
height:0px;
display:none;
}
.alert_lite_maximize {
width:0px;
height:0px;
display:none;
}
.alert_lite_title {
width:0px;
height:0px;
display:none;
}
.alert_lite_content {
overflow:auto;
color: #000;
font-family: Tahoma, Arial, sans-serif;
font-size: 10px;
background: #FFF;
}
/* For alert/confirm dialog */
.alert_lite_window {
border:1px solid #F00;
background: #FFF;
padding:20px;
margin-left:auto;
margin-right:auto;
width:400px;
}
.alert_lite_message {
font-size:16px;
text-align:center;
width:100%;
color:#F00;
padding-bottom:10px;
}
.alert_lite_buttons {
text-align:center;
width:100%;
}
.alert_lite_buttons input {
width:20%;
margin:10px;
}
.alert_lite_progress {
float:left;
margin:auto;
text-align:center;
width:100%;
height:16px;
background: #FFF url('alert/progress.gif') no-repeat center center
}
table.alert_lite_header {
border:1px solid #F00;
background:#FFF
}

View File

@ -1,150 +1,150 @@
.overlay_alphacube {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.alphacube_nw {
background: transparent url(alphacube/left-top.gif) no-repeat 0 0;
width:10px;
height:25px;
}
.alphacube_n {
background: transparent url(alphacube/top-middle.gif) repeat-x 0 0;
height:25px;
}
.alphacube_ne {
background: transparent url(alphacube/right-top.gif) no-repeat 0 0;
width:10px;
height:25px;
}
.alphacube_w {
background: transparent url(alphacube/frame-left.gif) repeat-y top left;
width:7px;
}
.alphacube_e {
background: transparent url(alphacube/frame-right.gif) repeat-y top right;
width:7px;
}
.alphacube_sw {
background: transparent url(alphacube/bottom-left-c.gif) no-repeat 0 0;
width:7px;
height:7px;
}
.alphacube_s {
background: transparent url(alphacube/bottom-middle.gif) repeat-x 0 0;
height:7px;
}
.alphacube_se, .alphacube_sizer {
background: transparent url(alphacube/bottom-right-c.gif) no-repeat 0 0;
width:7px;
height:7px;
}
.alphacube_sizer {
cursor:se-resize;
}
.alphacube_close {
width: 23px;
height: 23px;
background: transparent url(alphacube/button-close-focus.gif) no-repeat 0 0;
position:absolute;
top:0px;
right:11px;
cursor:pointer;
z-index:1000;
}
.alphacube_minimize {
width: 23px;
height: 23px;
background: transparent url(alphacube/button-min-focus.gif) no-repeat 0 0;
position:absolute;
top:0px;
right:55px;
cursor:pointer;
z-index:1000;
}
.alphacube_maximize {
width: 23px;
height: 23px;
background: transparent url(alphacube/button-max-focus.gif) no-repeat 0 0;
position:absolute;
top:0px;
right:33px;
cursor:pointer;
z-index:1000;
}
.alphacube_title {
float:left;
height:14px;
font-size:14px;
text-align:center;
margin-top:2px;
width:100%;
color:#123456;
}
.alphacube_content {
overflow:auto;
color: #000;
font-family: Tahoma, Arial, sans-serif;
font: 12px arial;
background:#FDFDFD;
}
/* For alert/confirm dialog */
.alphacube_window {
border:1px solid #F00;
background: #FFF;
padding:20px;
margin-left:auto;
margin-right:auto;
width:400px;
}
.alphacube_message {
font: 12px arial;
text-align:center;
width:100%;
padding-bottom:10px;
}
.alphacube_buttons {
text-align:center;
width:100%;
}
.alphacube_buttons input {
width:20%;
margin:10px;
}
.alphacube_progress {
float:left;
margin:auto;
text-align:center;
width:100%;
height:16px;
background: #FFF url('alert/progress.gif') no-repeat center center
}
.alphacube_wired_frame {
background: #FFF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.overlay_alphacube {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.alphacube_nw {
background: transparent url(alphacube/left-top.gif) no-repeat 0 0;
width:10px;
height:25px;
}
.alphacube_n {
background: transparent url(alphacube/top-middle.gif) repeat-x 0 0;
height:25px;
}
.alphacube_ne {
background: transparent url(alphacube/right-top.gif) no-repeat 0 0;
width:10px;
height:25px;
}
.alphacube_w {
background: transparent url(alphacube/frame-left.gif) repeat-y top left;
width:7px;
}
.alphacube_e {
background: transparent url(alphacube/frame-right.gif) repeat-y top right;
width:7px;
}
.alphacube_sw {
background: transparent url(alphacube/bottom-left-c.gif) no-repeat 0 0;
width:7px;
height:7px;
}
.alphacube_s {
background: transparent url(alphacube/bottom-middle.gif) repeat-x 0 0;
height:7px;
}
.alphacube_se, .alphacube_sizer {
background: transparent url(alphacube/bottom-right-c.gif) no-repeat 0 0;
width:7px;
height:7px;
}
.alphacube_sizer {
cursor:se-resize;
}
.alphacube_close {
width: 23px;
height: 23px;
background: transparent url(alphacube/button-close-focus.gif) no-repeat 0 0;
position:absolute;
top:0px;
right:11px;
cursor:pointer;
z-index:1000;
}
.alphacube_minimize {
width: 23px;
height: 23px;
background: transparent url(alphacube/button-min-focus.gif) no-repeat 0 0;
position:absolute;
top:0px;
right:55px;
cursor:pointer;
z-index:1000;
}
.alphacube_maximize {
width: 23px;
height: 23px;
background: transparent url(alphacube/button-max-focus.gif) no-repeat 0 0;
position:absolute;
top:0px;
right:33px;
cursor:pointer;
z-index:1000;
}
.alphacube_title {
float:left;
height:14px;
font-size:14px;
text-align:center;
margin-top:2px;
width:100%;
color:#123456;
}
.alphacube_content {
overflow:auto;
color: #000;
font-family: Tahoma, Arial, sans-serif;
font: 12px arial;
background:#FDFDFD;
}
/* For alert/confirm dialog */
.alphacube_window {
border:1px solid #F00;
background: #FFF;
padding:20px;
margin-left:auto;
margin-right:auto;
width:400px;
}
.alphacube_message {
font: 12px arial;
text-align:center;
width:100%;
padding-bottom:10px;
}
.alphacube_buttons {
text-align:center;
width:100%;
}
.alphacube_buttons input {
width:20%;
margin:10px;
}
.alphacube_progress {
float:left;
margin:auto;
text-align:center;
width:100%;
height:16px;
background: #FFF url('alert/progress.gif') no-repeat center center
}
.alphacube_wired_frame {
background: #FFF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}

View File

@ -1,51 +1,51 @@
<public:component>
<public:attach event="onpropertychange" onevent="propertyChanged()" />
<script>
var supported = /MSIE (5\.5)|[6789]/.test(navigator.userAgent) && navigator.platform == "Win32";
var realSrc;
var blankSrc = "blank.gif";
if (supported) fixImage();
function propertyChanged() {
if (!supported) return;
var pName = event.propertyName;
if (pName != "src") return;
// if not set to blank
if ( ! new RegExp(blankSrc).test(src))
fixImage();
};
function fixImage() {
// get src
var src = element.src;
// check for real change
if (src == realSrc) {
element.src = blankSrc;
return;
}
if ( ! new RegExp(blankSrc).test(src)) {
// backup old src
realSrc = src;
}
// test for png
if ( /\.png$/.test( realSrc.toLowerCase() ) ) {
// set blank image
element.src = blankSrc;
// set filter
element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
src + "',sizingMethod='scale')";
}
else {
// remove filter
element.runtimeStyle.filter = "";
}
}
</script>
<public:component>
<public:attach event="onpropertychange" onevent="propertyChanged()" />
<script>
var supported = /MSIE (5\.5)|[6789]/.test(navigator.userAgent) && navigator.platform == "Win32";
var realSrc;
var blankSrc = "blank.gif";
if (supported) fixImage();
function propertyChanged() {
if (!supported) return;
var pName = event.propertyName;
if (pName != "src") return;
// if not set to blank
if ( ! new RegExp(blankSrc).test(src))
fixImage();
};
function fixImage() {
// get src
var src = element.src;
// check for real change
if (src == realSrc) {
element.src = blankSrc;
return;
}
if ( ! new RegExp(blankSrc).test(src)) {
// backup old src
realSrc = src;
}
// test for png
if ( /\.png$/.test( realSrc.toLowerCase() ) ) {
// set blank image
element.src = blankSrc;
// set filter
element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
src + "',sizingMethod='scale')";
}
else {
// remove filter
element.runtimeStyle.filter = "";
}
}
</script>
</public:component>

View File

@ -1,121 +1,121 @@
.overlay_darkX {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.darkX_nw {
background: transparent url(darkX/titlebar-left-focused.png) no-repeat 0 0;
width:6px;
height:21px;
}
.darkX_n {
background: transparent url(darkX/titlebar-mid-focused.png) repeat-x 0 0;
height:21px;
}
.darkX_ne {
background: transparent url(darkX/titlebar-right-focused.png) no-repeat 0 0;
width:6px;
height:21px;
}
.darkX_w {
background: transparent url(darkX/frame-left-focused.png) repeat-y top left;
width:3px;
}
.darkX_e {
background: transparent url(darkX/frame-right-focused.png) repeat-y top right;
width:3px;
}
.darkX_sw {
background: transparent url(darkX/frame-bottom-left-focused.png) no-repeat 0 0;
width:5px;
height:3px;
}
.darkX_s {
background: transparent url(darkX/frame-bottom-mid-focused.png) repeat-x 0 0;
height:3px;
}
.darkX_se, .darkX_sizer {
background: transparent url(darkX/frame-bottom-right-focused.png) no-repeat 0 0;
width:5px;
height:3px;
}
.darkX_sizer {
cursor:se-resize;
}
.darkX_close {
width: 21px;
height: 21px;
background: transparent url(darkX/button-close-focused.png) no-repeat 0 0;
position:absolute;
top:0px;
right:5px;
cursor:pointer;
z-index:1000;
}
.darkX_minimize {
width: 21px;
height: 21px;
background: transparent url(darkX/button-minimize-focused.png) no-repeat 0 0;
position:absolute;
top:0px;
right:26px;
cursor:pointer;
z-index:1000;
}
.darkX_maximize {
width: 21px;
height: 21px;
background: transparent url(darkX/button-maximize-focused.png) no-repeat 0 0;
position:absolute;
top:0px;
right:47px;
cursor:pointer;
z-index:1000;
}
.darkX_title {
float:left;
height:14px;
font-size:12px;
text-align:center;
margin-top:2px;
width:100%;
color:#FFF;
}
.darkX_content {
overflow:auto;
color: #E6DF2A;
font-family: Tahoma, Arial, sans-serif;
font-size: 14px;
background:#5E5148;
}
/* FOR IE */
* html .darkX_minimize {
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-minimize-focused.png", sizingMethod="crop");
}
* html .darkX_maximize {
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-maximize-focused.png", sizingMethod="scale");
}
* html .darkX_close {
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-close-focused.png", sizingMethod="crop");
}
.overlay_darkX {
background-color: #85BBEF;
filter:alpha(opacity=60);
-moz-opacity: 0.6;
opacity: 0.6;
}
.darkX_nw {
background: transparent url(darkX/titlebar-left-focused.png) no-repeat 0 0;
width:6px;
height:21px;
}
.darkX_n {
background: transparent url(darkX/titlebar-mid-focused.png) repeat-x 0 0;
height:21px;
}
.darkX_ne {
background: transparent url(darkX/titlebar-right-focused.png) no-repeat 0 0;
width:6px;
height:21px;
}
.darkX_w {
background: transparent url(darkX/frame-left-focused.png) repeat-y top left;
width:3px;
}
.darkX_e {
background: transparent url(darkX/frame-right-focused.png) repeat-y top right;
width:3px;
}
.darkX_sw {
background: transparent url(darkX/frame-bottom-left-focused.png) no-repeat 0 0;
width:5px;
height:3px;
}
.darkX_s {
background: transparent url(darkX/frame-bottom-mid-focused.png) repeat-x 0 0;
height:3px;
}
.darkX_se, .darkX_sizer {
background: transparent url(darkX/frame-bottom-right-focused.png) no-repeat 0 0;
width:5px;
height:3px;
}
.darkX_sizer {
cursor:se-resize;
}
.darkX_close {
width: 21px;
height: 21px;
background: transparent url(darkX/button-close-focused.png) no-repeat 0 0;
position:absolute;
top:0px;
right:5px;
cursor:pointer;
z-index:1000;
}
.darkX_minimize {
width: 21px;
height: 21px;
background: transparent url(darkX/button-minimize-focused.png) no-repeat 0 0;
position:absolute;
top:0px;
right:26px;
cursor:pointer;
z-index:1000;
}
.darkX_maximize {
width: 21px;
height: 21px;
background: transparent url(darkX/button-maximize-focused.png) no-repeat 0 0;
position:absolute;
top:0px;
right:47px;
cursor:pointer;
z-index:1000;
}
.darkX_title {
float:left;
height:14px;
font-size:12px;
text-align:center;
margin-top:2px;
width:100%;
color:#FFF;
}
.darkX_content {
overflow:auto;
color: #E6DF2A;
font-family: Tahoma, Arial, sans-serif;
font-size: 14px;
background:#5E5148;
}
/* FOR IE */
* html .darkX_minimize {
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-minimize-focused.png", sizingMethod="crop");
}
* html .darkX_maximize {
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-maximize-focused.png", sizingMethod="scale");
}
* html .darkX_close {
background-color: transparent;
background-image: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="../themes/darkX/button-close-focused.png", sizingMethod="crop");
}

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