diff --git a/Gemfile b/Gemfile index 58ccbc011..52a6cae8d 100644 --- a/Gemfile +++ b/Gemfile @@ -63,22 +63,18 @@ group :development do if RUBY_PLATFORM =~ /w32/ gem 'win32console' end + + if RUBY_PLATFORM =~ /darwin/ + gem 'puma' +end end group :development, :test do - unless RUBY_PLATFORM =~ /w32/ - gem 'pry-rails' if RUBY_VERSION >= '2.0.0' gem 'pry-byebug' + gem "test-unit", "~>3.0" end - gem 'pry-stack_explorer' - if RUBY_PLATFORM =~ /darwin/ - gem 'puma' - end - end - gem 'rspec-rails', '~> 3.0' - gem 'factory_girl_rails' end # Gems used only for assets and not required diff --git a/app/api/mobile/entities/attachment.rb b/app/api/mobile/entities/attachment.rb index 0eda2d1c0..55e4aa555 100644 --- a/app/api/mobile/entities/attachment.rb +++ b/app/api/mobile/entities/attachment.rb @@ -17,17 +17,18 @@ module Mobile end else case field + when :download_url + "attachments/download/#{f.try(:id)}" when :file_dir "attachments/download/" << f.send(:id).to_s << '/' when :attafile_size (number_to_human_size(f.filesize)).gsub("ytes", "").to_s when :coursename - f.course.nil? ? "" : f.course.name + f.try(:course).try(:name) || '' when :syllabus_title - f.course.nil? ? "" : f.course.syllabus.nil? ? "" : f.course.syllabus.title + f.try(:course).try(:syllabus).try(:title) || '' when :course_id - f.course.nil? ? 0 : f.course.id - + f.try(:course).try(:id) || 0 end end end @@ -50,6 +51,8 @@ module Mobile current_user_is_teacher = is_course_teacher(current_user,instance.course) current_user_is_teacher end + + attachment_expose :download_url end end end \ No newline at end of file diff --git a/app/api/mobile/entities/issue.rb b/app/api/mobile/entities/issue.rb index 7f09906b5..2cb6cd87a 100644 --- a/app/api/mobile/entities/issue.rb +++ b/app/api/mobile/entities/issue.rb @@ -180,6 +180,8 @@ module Mobile end end end + + expose :attachments, using: Mobile::Entities::Attachment end end end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 14d5961cd..07b49755d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -117,6 +117,9 @@ class ApplicationController < ActionController::Base elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth? # RSS key authentication does not start a session user = User.find_by_rss_key(params[:key]) + elsif session[:wechat_openid] + uw = UserWechat.find_by_openid(session[:wechat_openid]) + user = uw.user if uw end end if user.nil? && Setting.rest_api_enabled? && accept_api_auth? @@ -509,8 +512,7 @@ class ApplicationController < ActionController::Base # render_404 # end - def self. - model_object(model) + def self.model_object(model) self.model_object = model end diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index e7a5d3164..848d85c34 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -72,8 +72,10 @@ class AttachmentsController < ApplicationController def direct_download @attachment.increment_download + file_type = detect_content_type(@attachment) + send_file @attachment.diskfile, :filename => filename_for_content_disposition(@attachment.filename), - :type => detect_content_type(@attachment), + :type => file_type, :disposition => 'attachment' #inline can open in browser end @@ -130,11 +132,7 @@ class AttachmentsController < ApplicationController def download # modify by nwb # 下载添加权限设置 - if (params[:type] && params[:type] == "wechat" ) - candown = true - else - candown = attachment_candown @attachment - end + candown = attachment_candown @attachment if candown || User.current.admin? || User.current.id == @attachment.author_id if stale?(:etag => @attachment.digest) diff --git a/app/controllers/wechats_controller.rb b/app/controllers/wechats_controller.rb index 654f83855..41653faf9 100644 --- a/app/controllers/wechats_controller.rb +++ b/app/controllers/wechats_controller.rb @@ -1,4 +1,6 @@ #coding=utf-8 + +require 'base64' class WechatsController < ActionController::Base wechat_responder @@ -105,6 +107,7 @@ class WechatsController < ActionController::Base end on :event, with: 'unsubscribe' do |request| + unBind(request) request.reply.success # user can not receive this message end @@ -272,12 +275,17 @@ class WechatsController < ActionController::Base end end + def unBind(request) + uw = user_binded?(request[:FromUserName]) + uw.try(:subscribe!) + end + def sendBind(request) tmpurl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{ROOT_URL+'/wechat/user_activities'}&response_type=code&scope=snsapi_base&state=login&connect_redirect=1#wechat_redirect" logger.info "tmpurl!!!!!!!!!!!!!!" logger.info tmpurl news = (1..1).each_with_object([]) { |n, memo| memo << { title: '绑定登录', content: "欢迎使用Trustie创新实践服务平台! -在这里您可以随时了解您的课程和项目动态,随时点赞和回复。 +在这里您可以随时了解您的课程和项目动态,随时点赞和回复。交作业、代码提交等更多功能,请前往 www.trustie.net 我们将会与微信不断结合,为您提供更有价值的服务。 您还未绑定确实的用户,请先绑定,谢谢!" } } @@ -485,9 +493,33 @@ class WechatsController < ActionController::Base end + # 用于权限跳转 + def auth + state = params[:state] + url = "#{ROOT_URL}/wechat/auth_callback" + authorize_url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=#{Wechat.config.appid}&redirect_uri=#{url}&response_type=code&scope=snsapi_base&state=#{state}&connect_redirect=1#wechat_redirect" + redirect_to authorize_url + end + + def auth_callback + path = Base64.urlsafe_decode64(params[:state]) + open_id = get_openid_from_code(params[:code]) + unless open_id + render 'wechats/open_wechat', layout: nil and return + end + + redirect_to "/wechat/user_activities##{path}" + end + + private def get_openid_from_code(code) - return 'oCnvgvz8R7QheXE-R9Kkr39j8Ndg' if code =='only-for-test' + if code =='only-for-test' + openid = 'o3ss_wHOOnHkz1khBJxH8RF4SfPY' + session[:wechat_openid] = openid + return openid + end + openid = session[:wechat_openid] unless openid if code diff --git a/app/models/attachment.rb b/app/models/attachment.rb index c83a7bf2a..ff87f533c 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -397,7 +397,7 @@ class Attachment < ActiveRecord::Base end def course - container + Course === container ? container : nil end def visible?(user=User.current) diff --git a/app/models/user_wechat.rb b/app/models/user_wechat.rb index c63411ab3..71221a3d1 100644 --- a/app/models/user_wechat.rb +++ b/app/models/user_wechat.rb @@ -24,4 +24,9 @@ class UserWechat < ActiveRecord::Base BlogComment.where(author_id: old_user).update_all(author_id: u.id) UserActivity.where(user_id: old_user).update_all(user_id: u.id) end + + + def unsubscribe! + self.delete + end end diff --git a/config/application.rb b/config/application.rb index 4b25d4278..88c6a43df 100644 --- a/config/application.rb +++ b/config/application.rb @@ -57,7 +57,7 @@ module RedmineApp #disable [deprecated] I18n.enforce_available_locales will default to true in the future. # If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false # to avoid this message. - #I18n.config.enforce_available_locales = false + I18n.config.enforce_available_locales = false # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] diff --git a/config/initializers/mini_profiler.rb b/config/initializers/mini_profiler.rb new file mode 100644 index 000000000..48fbe1423 --- /dev/null +++ b/config/initializers/mini_profiler.rb @@ -0,0 +1,2 @@ +Rack::MiniProfiler.config.position = 'right' +Rack::MiniProfiler.config.start_hidden = true \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 98c12d83b..616ce675c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1414,6 +1414,8 @@ RedmineApp::Application.routes.draw do post :bind post :get_bind post :is_bind + get :auth + get :auth_callback end end diff --git a/public/assets/wechat/app.html b/public/assets/wechat/app.html index 232f07494..53b82c277 100644 --- a/public/assets/wechat/app.html +++ b/public/assets/wechat/app.html @@ -55,7 +55,6 @@ - \ No newline at end of file diff --git a/public/assets/wechat/issue_detail.html b/public/assets/wechat/issue_detail.html index 278b545a5..c2fc649c1 100644 --- a/public/assets/wechat/issue_detail.html +++ b/public/assets/wechat/issue_detail.html @@ -27,6 +27,18 @@
{{issue.project_name}} - 项目问题{{issue.created_on}}
+ + + + +
+
+ +
+
+ 状   态:{{issue.issue_status}} 优先级:{{issue.issue_priority}}
指派给:{{issue.issue_assigned_to}} @@ -114,7 +126,7 @@
- +
@@ -124,4 +136,15 @@
- \ No newline at end of file + + + +
+
+
+ +
+ +
diff --git a/public/images/wechat/w-icons-file.png b/public/images/wechat/w-icons-file.png new file mode 100644 index 000000000..c5b8be58b Binary files /dev/null and b/public/images/wechat/w-icons-file.png differ diff --git a/public/javascripts/homework.js b/public/javascripts/homework.js index b868b25d0..26d1e5bd8 100644 --- a/public/javascripts/homework.js +++ b/public/javascripts/homework.js @@ -477,7 +477,7 @@ $(function(){ //注意\n\ //1:该程序每次运行的时间必须小于200毫秒,否则会超时,程序超时将不会测试剩余的测试集\n\ //2:该程序每次运行使用的内存不能超过1M,否则会返回错误\n\ - //3:该程序每次运行输出的结果最多显示100个字符(多余的不显示),每行末尾的所有空格用□表示\n\ + //3:该程序每次运行输出的结果最多显示1000个字符(多余的不显示),每行末尾的所有空格用□表示\n\ import java.io.*;\n\ import java.util.*;\n\ \n\ @@ -501,7 +501,7 @@ class Main\n\ //注意\n\ //1:该程序每次运行的时间必须小于200毫秒,否则会超时,程序超时将不会测试剩余的测试集\n\ //2:该程序每次运行使用的内存不能超过1M,否则会返回错误\n\ - //3:该程序每次运行输出的结果最多显示100个字符(多余的不显示),每行末尾的所有空格用□表示\n\ + //3:该程序每次运行输出的结果最多显示1000个字符(多余的不显示),每行末尾的所有空格用□表示\n\ #include \n\ int main()\n\ {\n\ @@ -521,7 +521,7 @@ int main()\n\ //注意\n\ //1:该程序每次运行的时间必须小于200毫秒,否则会超时,程序超时将不会测试剩余的测试集\n\ //2:该程序每次运行使用的内存不能超过1M,否则会返回错误\n\ - //3:该程序每次运行输出的结果最多显示100个字符(多余的不显示),每行末尾的所有空格用□表示\n\ + //3:该程序每次运行输出的结果最多显示1000个字符(多余的不显示),每行末尾的所有空格用□表示\n\ #include \n\ using namespace std;\n\ \n\ @@ -543,7 +543,7 @@ int main()\n\ #注意\n\ #1:该程序每次运行的时间必须小于200毫秒,否则会超时,程序超时将不会测试剩余的测试集\n\ #2:该程序每次运行使用的内存不能超过1M,否则会返回错误\n\ - #3:该程序每次运行输出的结果最多显示100个字符(多余的不显示),空格用□表示\n\ + #3:该程序每次运行输出的结果最多显示1000个字符(多余的不显示),空格用□表示\n\ import sys \n\ \n\ #获取参数方式,使用raw_input\n\ diff --git a/public/javascripts/wechat/app.js b/public/javascripts/wechat/app.js index 00d6bf3bc..385d0fd8f 100644 --- a/public/javascripts/wechat/app.js +++ b/public/javascripts/wechat/app.js @@ -10,6 +10,7 @@ app.constant('config', { app.run(['$rootScope', 'auth', '$location', '$routeParams', function($rootScope, auth, $location, $routeParams){ if(g_redirect_path && g_redirect_path.length>1){ + console.log(g_redirect_path); $location.path(g_redirect_path); g_redirect_path = null; } diff --git a/public/javascripts/wechat/controllers/issue.js b/public/javascripts/wechat/controllers/issue.js index b580da465..40a2f0589 100644 --- a/public/javascripts/wechat/controllers/issue.js +++ b/public/javascripts/wechat/controllers/issue.js @@ -1,4 +1,54 @@ -app.controller('IssueController', ['$scope', '$http', '$routeParams', 'auth', 'common', function($scope, $http, $routeParams, auth, common){ +app.controller('IssueController', ['$scope', '$http', '$routeParams', 'auth', 'common', + function($scope, $http, $routeParams, auth, common){ + + var vm = $scope; + vm.previewImgUrls = []; + vm.showAtDialog = false; + + var parseAtPersons = function (comment) { + var selectedPersons = []; + var ss = comment.match(/@(.+?)\s+/g); + + for(var i in ss){ + var personName = ss[i].substr(1, ss[i].length-2); + console.log(personName); + + for(var j in vm.at_persons){ + var person = vm.at_persons[j]; + if(person.name == personName){ + selectedPersons.push(person); + } + } + } + + for(var i in selectedPersons){ + var person = selectedPersons[i]; + comment = comment.replace('@'+person.name+' ', + '@'+person.name+'('+person.login+')'+' ' + ); + } + return comment; + }; + + vm.onPostChange = function (newValue, oldValue) { + if(newValue.length > oldValue.length && newValue.match(/@$/)=='@'){ + console.log('@ fire'); + vm.showAtDialog = true; + if(!vm.at_persons){ + $http.get('/at/'+$routeParams.id+'.json?type=Issue').then(function (response) { + vm.at_persons = response.data; + }); + } + } + console.log(vm.issue.comment); + }; + + vm.selectAtPerson = function (index) { + var person = vm.at_persons[index]; + vm.showAtDialog = false; + vm.issue.comment += person.name + ' '; + }; common.init({ id: $routeParams.id, @@ -18,11 +68,28 @@ app.controller('IssueController', ['$scope', '$http', '$routeParams', 'auth', 'c replytype = data.type; page = data.page; + + var parseImgAttachment = function(attachments){ + var urls = []; + if(!attachments){ + return urls; + } + + for(var i = attachments.length-1; i>=0; i--){ + if(attachments[i].filename.match('.jpg$')=='.jpg' || attachments[i].filename.match('.png$')=='.png'){ + urls.push(attachments[i].download_url); + attachments.splice(i, 1); + } + } + return urls; + }; + if (replytype == 0){ if (page == 0){ $scope.issue = data.data; $scope.page = 0; $scope.is_public = data.is_public; + $scope.previewImgUrls = parseImgAttachment($scope.issue.attachments); } else{ $scope.issue.all_children = $scope.issue.all_children.concat(data.data.all_children); @@ -42,6 +109,17 @@ app.controller('IssueController', ['$scope', '$http', '$routeParams', 'auth', 'c } }, replyCallback: function(){ + }, + beforeReplay: function(data){ + return parseAtPersons(data); } }); + + $scope.previewImg = function(index){ + console.log(index); + wx.previewImage({ + current: $scope.previewImgUrls[index], // 当前显示图片的http链接 + urls: $scope.previewImgUrls // 需要预览的图片http链接列表 + }); + } }]); \ No newline at end of file diff --git a/public/javascripts/wechat/others/factory.js b/public/javascripts/wechat/others/factory.js index 190dd36b4..daf8a45f8 100644 --- a/public/javascripts/wechat/others/factory.js +++ b/public/javascripts/wechat/others/factory.js @@ -94,24 +94,17 @@ app.factory('rms', function(){ }); app.factory('common', ['$http', 'auth', '$routeParams','rms','config','wx','$location', function($http, auth, $routeParams,rms,config,wx,$location){ - var addCommonReply = function(id, type, data,args,reply_type, cb){ - //先判断有没有绑定 -// $http.post( -// '/wechat/is_bind', -// {} ///不用传code了,都由服务器来处理 -// ).then(function(response){ -// console.log(response.data); -// if(response.data.status != 0){ -// $location.path("/login_tip"); -// } -// }); - + var addCommonReply = function(id, type, data,args,reply_type, beforeReply, cb){ if(!data.comment || data.comment.length<=0){ return; } var temp = data.comment.replace(/\n/g,'
'); + if(typeof beforeReply==='function'){ + temp = beforeReply(temp); + } + var userInfo = { type: type, content: temp, @@ -121,6 +114,8 @@ app.factory('common', ['$http', 'auth', '$routeParams','rms','config','wx','$loc //回复按钮禁用 data.disabled = true; + console.log(userInfo); + $http({ method: 'POST', url: apiUrl+ "new_comment/"+id, @@ -308,7 +303,8 @@ app.factory('common', ['$http', 'auth', '$routeParams','rms','config','wx','$loc loadData(args.id,0,0); args.scope.addReply = function(data,reply_type){ console.log(data.comment); - addCommonReply(data.act_id, args.replyType, data,args,reply_type, function(subscribe){ + + addCommonReply(data.act_id, args.replyType, data,args, reply_type, args.beforeReplay, function(subscribe){ // args.scope.formData = {comment: ''}; if(subscribe == 0){ $location.path("/login_tip"); diff --git a/public/stylesheets/weui/weixin.css b/public/stylesheets/weui/weixin.css index aa4cff972..a485d94bb 100644 --- a/public/stylesheets/weui/weixin.css +++ b/public/stylesheets/weui/weixin.css @@ -1,6 +1,8 @@ @charset "utf-8"; /* CSS Document */ +html, body{ margin:0; height:100%; } + /*基本样式*/ body,table,input,textarea,select,button { font-family: "微软雅黑","宋体","Helvetica Neue", Helvetica, Arial, sans-serif;} body, ul, h1,h2,h3,h4,h5,p,pre,input {padding:0px; margin:0px;} @@ -263,4 +265,22 @@ a.underline {text-decoration:underline;} /*资料修改*/ .blank-row {width:100%; height:38px; line-height:38px; vertical-align:middle;} .upload-input-container {width:30px; height:30px; border:1px solid #ddd; position:relative;} -.upload-input {width:30px; height:30px; position:absolute; z-index:1; opacity:0;} \ No newline at end of file +.upload-input {width:30px; height:30px; position:absolute; z-index:1; opacity:0;} +.select-container {position:relative; padding-left:62px;} +.select-text {position:absolute; left:15px;} +.select-model {width:100%; border:none; color:#999; margin-top:8px;} + + +/*附件显示 20161202byLB*/ +.clear:after{clear:both;content:".";display:block;font-size:0;height:0;line-height:0;visibility:hidden} +.clear{clear:both;zoom:1} +.weixin-files{ width:100%; word-break:break-all; word-wrap: break-word;} +.weixin-files ol{-webkit-padding-start: 0px;} +.weixin-files-img{ border:1px solid #eee; padding:2px; width:50px; margin-bottom:5px;} +/*所有人 20161202byLB*/ +.weixin-users-all{ width:100%; background:#fff;} +.weixin-users-all li{ height:40px; line-height:40px; font-size:14px; color:#888; padding:0 15px; border-bottom:1px solid #ccc;} +.weixin-users-all li:hover{ background:#f4f4f4;} + +/*弹出@选择对话框 guange*/ +.wechat-at {position: fixed; top: 0; left:0; width: 100%; height: 100%; background-color: #ffffff;} \ No newline at end of file