From a51ba0c10466052fa8e5c2d017600ed7a5863aab Mon Sep 17 00:00:00 2001 From: alanlong9278 <547533434@qq.com> Date: Wed, 15 Apr 2015 12:30:37 +0800 Subject: [PATCH 1/8] Signed-off-by: alanlong9278 <547533434@qq.com> --- .gitignore | 50 +- Gemfile | 282 +-- app/controllers/discuss_demos_controller.rb | 84 +- app/models/user.rb | 2058 ++++++++--------- app/models/user_extensions.rb | 172 +- app/views/attachments/destroy.js.erb | 14 +- app/views/bids/_bid_homework_show.html.erb | 400 ++-- app/views/issues/index.html.erb | 278 +-- app/views/layouts/_base_feedback.html.erb | 394 ++-- app/views/poll/publish_poll.js.erb | 18 +- app/views/poll/republish_poll.js.erb | 18 +- app/views/repositories/newrepo.html.erb | 14 +- app/views/tags/_tag_name.html.erb | 218 +- app/views/watchers/_set_watcher.js.erb | 22 +- app/views/welcome/_hot_projects_list.html.erb | 48 +- lib/git_refresh_changes.py | 60 +- plugins/redmine_ckeditor/Gemfile | 12 +- public/javascripts/application.js | 1734 +++++++------- 18 files changed, 2938 insertions(+), 2938 deletions(-) diff --git a/.gitignore b/.gitignore index bc936c34c..2b9fa95fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,25 @@ -*.swp -/.project -/.idea -/.bundle -*.swp -/config/database.yml -/config/configuration.yml -/files/* -/log/* -/public/tmp/* -/tmp/* -/public/cache/* -.gitignore -/config/newrelic.yml -/public/images/avatars/* -/Gemfile -/Gemfile.lock -/db/schema.rb -/Gemfile.lock -/lib/plugins/acts_as_versioned/test/debug.log -.rbenv-gemsets -.DS_Store -public/api_doc/ -/.metadata -vendor/cache +*.swp +/.project +/.idea +/.bundle +*.swp +/config/database.yml +/config/configuration.yml +/files/* +/log/* +/public/tmp/* +/tmp/* +/public/cache/* +.gitignore +/config/newrelic.yml +/public/images/avatars/* +/Gemfile +/Gemfile.lock +/db/schema.rb +/Gemfile.lock +/lib/plugins/acts_as_versioned/test/debug.log +.rbenv-gemsets +.DS_Store +public/api_doc/ +/.metadata +vendor/cache diff --git a/Gemfile b/Gemfile index 29b2716a9..48c5adc0e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,141 +1,141 @@ -source 'http://ruby.taobao.org' -#source 'http://ruby.sdutlinux.org/' - -unless RUBY_PLATFORM =~ /w32/ - # unix-like only - gem 'iconv' - gem 'rubyzip' - gem 'zip-zip' -end - -gem 'grape', '~> 0.9.0' -gem 'grape-entity' -gem 'seems_rateable', '~> 1.0.13' -gem "rails", "3.2.13" -gem "jquery-rails", "~> 2.0.2" -gem "i18n", "~> 0.6.0" -gem 'coderay', '~> 1.1.0' -gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby] -gem "builder", "3.0.0" -gem 'acts-as-taggable-on', '2.4.1' -gem 'spreadsheet' -gem 'ruby-ole' -#gem 'email_verifier', path: 'lib/email_verifier' -gem 'rufus-scheduler' -#gem 'dalli', path: 'lib/dalli-2.7.2' -gem 'rails_kindeditor' -group :development do - gem 'grape-swagger' - #gem 'grape-swagger-ui', git: 'https://github.com/guange2015/grape-swagger-ui.git' - gem 'puma' if RbConfig::CONFIG['host_os'] =~ /linux/ - gem 'pry-rails' - if RUBY_VERSION >= '2.0.0' - gem 'pry-byebug' - else - gem 'pry-debugger' - end - gem 'pry-stack_explorer' - gem 'better_errors', '~> 1.1.0' - gem 'rack-mini-profiler', '~> 0.9.3' -end - -group :test do - gem "shoulda", "~> 3.5.0" - gem "mocha", "~> 1.1.0" - gem 'capybara', '~> 2.4.1' - gem 'nokogiri', '~> 1.6.3' - gem 'factory_girl', '~> 4.4.0' - gem 'selenium-webdriver', '~> 2.42.0' - - gem "faker" - # platforms :mri, :mingw do - # group :rmagick do - # # RMagick 2 supports ruby 1.9 - # # RMagick 1 would be fine for ruby 1.8 but Bundler does not support - # # different requirements for the same gem on different platforms - # gem "rmagick", ">= 2.0.0" - # end - #end -end - - # gem 'rspec-rails' , '2.13.1' - # gem 'guard-rspec','2.5.0' -# Gems used only for assets and not required -# in production environments by default. -group :assets do - gem 'sass-rails', '~> 3.2.3' - gem 'coffee-rails', '~> 3.2.1' - - # See https://github.com/sstephenson/execjs#readme for more supported runtimes - gem 'therubyracer', :platforms => :ruby - - gem 'uglifier', '>= 1.0.3' -end - -# Optional gem for LDAP authentication -group :ldap do - gem "net-ldap", "~> 0.3.1" -end - - -# Optional gem for OpenID authentication -group :openid do - gem "ruby-openid", "~> 2.1.4", :require => "openid" - gem "rack-openid" -end - -# Optional gem for exporting the gantt to a PNG file, not supported with jruby -platforms :jruby do - # jruby-openssl is bundled with JRuby 1.7.0 - gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0' - gem "activerecord-jdbc-adapter", "1.2.5" -end - -# Include database gems for the adapters found in the database -# configuration file -require 'erb' -require 'yaml' -database_file = File.join(File.dirname(__FILE__), "config/database.yml") -if File.exist?(database_file) - database_config = YAML::load(ERB.new(IO.read(database_file)).result) - adapters = database_config.values.map {|c| c['adapter']}.compact.uniq - if adapters.any? - adapters.each do |adapter| - case adapter - when 'mysql2' - gem "mysql2", "= 0.3.18", :platforms => [:mri, :mingw] - gem "activerecord-jdbcmysql-adapter", :platforms => :jruby - when 'mysql' - gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw] - gem "activerecord-jdbcmysql-adapter", :platforms => :jruby - when /postgresql/ - gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw] - gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby - when /sqlite3/ - gem "sqlite3", :platforms => [:mri, :mingw] - gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby - when /sqlserver/ - gem "tiny_tds", "~> 0.5.1", :platforms => [:mri, :mingw] - gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw] - else - warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems") - end - end - else - warn("No adapter found in config/database.yml, please configure it first") - end -else - warn("Please configure your config/database.yml first") -end - -local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") -if File.exists?(local_gemfile) - puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v` - instance_eval File.read(local_gemfile) -end - -# Load plugins' Gemfiles -Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file| - puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v` - instance_eval File.read(file) -end +source 'http://ruby.taobao.org' +#source 'http://ruby.sdutlinux.org/' + +unless RUBY_PLATFORM =~ /w32/ + # unix-like only + gem 'iconv' + gem 'rubyzip' + gem 'zip-zip' +end + +gem 'grape', '~> 0.9.0' +gem 'grape-entity' +gem 'seems_rateable', '~> 1.0.13' +gem "rails", "3.2.13" +gem "jquery-rails", "~> 2.0.2" +gem "i18n", "~> 0.6.0" +gem 'coderay', '~> 1.1.0' +gem "fastercsv", "~> 1.5.0", :platforms => [:mri_18, :mingw_18, :jruby] +gem "builder", "3.0.0" +gem 'acts-as-taggable-on', '2.4.1' +gem 'spreadsheet' +gem 'ruby-ole' +#gem 'email_verifier', path: 'lib/email_verifier' +gem 'rufus-scheduler' +#gem 'dalli', path: 'lib/dalli-2.7.2' +gem 'rails_kindeditor' +group :development do + gem 'grape-swagger' + #gem 'grape-swagger-ui', git: 'https://github.com/guange2015/grape-swagger-ui.git' + gem 'puma' if RbConfig::CONFIG['host_os'] =~ /linux/ + gem 'pry-rails' + if RUBY_VERSION >= '2.0.0' + gem 'pry-byebug' + else + gem 'pry-debugger' + end + gem 'pry-stack_explorer' + gem 'better_errors', '~> 1.1.0' + gem 'rack-mini-profiler', '~> 0.9.3' +end + +group :test do + gem "shoulda", "~> 3.5.0" + gem "mocha", "~> 1.1.0" + gem 'capybara', '~> 2.4.1' + gem 'nokogiri', '~> 1.6.3' + gem 'factory_girl', '~> 4.4.0' + gem 'selenium-webdriver', '~> 2.42.0' + + gem "faker" + # platforms :mri, :mingw do + # group :rmagick do + # # RMagick 2 supports ruby 1.9 + # # RMagick 1 would be fine for ruby 1.8 but Bundler does not support + # # different requirements for the same gem on different platforms + # gem "rmagick", ">= 2.0.0" + # end + #end +end + + # gem 'rspec-rails' , '2.13.1' + # gem 'guard-rspec','2.5.0' +# Gems used only for assets and not required +# in production environments by default. +group :assets do + gem 'sass-rails', '~> 3.2.3' + gem 'coffee-rails', '~> 3.2.1' + + # See https://github.com/sstephenson/execjs#readme for more supported runtimes + gem 'therubyracer', :platforms => :ruby + + gem 'uglifier', '>= 1.0.3' +end + +# Optional gem for LDAP authentication +group :ldap do + gem "net-ldap", "~> 0.3.1" +end + + +# Optional gem for OpenID authentication +group :openid do + gem "ruby-openid", "~> 2.1.4", :require => "openid" + gem "rack-openid" +end + +# Optional gem for exporting the gantt to a PNG file, not supported with jruby +platforms :jruby do + # jruby-openssl is bundled with JRuby 1.7.0 + gem "jruby-openssl" if Object.const_defined?(:JRUBY_VERSION) && JRUBY_VERSION < '1.7.0' + gem "activerecord-jdbc-adapter", "1.2.5" +end + +# Include database gems for the adapters found in the database +# configuration file +require 'erb' +require 'yaml' +database_file = File.join(File.dirname(__FILE__), "config/database.yml") +if File.exist?(database_file) + database_config = YAML::load(ERB.new(IO.read(database_file)).result) + adapters = database_config.values.map {|c| c['adapter']}.compact.uniq + if adapters.any? + adapters.each do |adapter| + case adapter + when 'mysql2' + gem "mysql2", "= 0.3.18", :platforms => [:mri, :mingw] + gem "activerecord-jdbcmysql-adapter", :platforms => :jruby + when 'mysql' + gem "mysql", "~> 2.8.1", :platforms => [:mri, :mingw] + gem "activerecord-jdbcmysql-adapter", :platforms => :jruby + when /postgresql/ + gem "pg", ">= 0.11.0", :platforms => [:mri, :mingw] + gem "activerecord-jdbcpostgresql-adapter", :platforms => :jruby + when /sqlite3/ + gem "sqlite3", :platforms => [:mri, :mingw] + gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby + when /sqlserver/ + gem "tiny_tds", "~> 0.5.1", :platforms => [:mri, :mingw] + gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw] + else + warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems") + end + end + else + warn("No adapter found in config/database.yml, please configure it first") + end +else + warn("Please configure your config/database.yml first") +end + +local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") +if File.exists?(local_gemfile) + puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v` + instance_eval File.read(local_gemfile) +end + +# Load plugins' Gemfiles +Dir.glob File.expand_path("../plugins/*/Gemfile", __FILE__) do |file| + puts "Loading #{file} ..." if $DEBUG # `ruby -d` or `bundle -v` + instance_eval File.read(file) +end diff --git a/app/controllers/discuss_demos_controller.rb b/app/controllers/discuss_demos_controller.rb index b0c9791cb..9a03f3b86 100644 --- a/app/controllers/discuss_demos_controller.rb +++ b/app/controllers/discuss_demos_controller.rb @@ -1,42 +1,42 @@ -class DiscussDemosController < ApplicationController - def index - - @discuss_demo_list = DiscussDemo.where("body is not null").order("created_at desc").page(params[:page] || 1).per(10) - end - - def new - @discuss_demo = DiscussDemo.create - @discuss_demo.save! - @discuss_demo - end - - def create - - end - - def update - @discuss_demo = DiscussDemo.find(params[:id]) - @discuss_demo.update_attributes(:title => params[:discuss_demo][:title],:body => params[:discuss_demo][:body]) - redirect_to :controller=> 'discuss_demos',:action => 'show',:id => params[:id] - end - - def delete - - end - - def destroy - asset = Kindeditor::Asset.find_by_owner_id(params[:id]) - if !asset.nil? - filepath = File.join(Rails.root,"public","files","uploads", - asset[:created_at].to_s.gsub("+0800","").to_datetime.strftime("%Y%m").to_s, - asset[:asset].to_s) - File.delete(filepath) if File.exist?filepath - end - DiscussDemo.destroy(params[:id]) - redirect_to :controller=> 'discuss_demos',:action => 'index' - end - - def show - @discuss_demo = DiscussDemo.find(params[:id]) - end -end +class DiscussDemosController < ApplicationController + def index + + @discuss_demo_list = DiscussDemo.where("body is not null").order("created_at desc").page(params[:page] || 1).per(10) + end + + def new + @discuss_demo = DiscussDemo.create + @discuss_demo.save! + @discuss_demo + end + + def create + + end + + def update + @discuss_demo = DiscussDemo.find(params[:id]) + @discuss_demo.update_attributes(:title => params[:discuss_demo][:title],:body => params[:discuss_demo][:body]) + redirect_to :controller=> 'discuss_demos',:action => 'show',:id => params[:id] + end + + def delete + + end + + def destroy + asset = Kindeditor::Asset.find_by_owner_id(params[:id]) + if !asset.nil? + filepath = File.join(Rails.root,"public","files","uploads", + asset[:created_at].to_s.gsub("+0800","").to_datetime.strftime("%Y%m").to_s, + asset[:asset].to_s) + File.delete(filepath) if File.exist?filepath + end + DiscussDemo.destroy(params[:id]) + redirect_to :controller=> 'discuss_demos',:action => 'index' + end + + def show + @discuss_demo = DiscussDemo.find(params[:id]) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 9abe80779..87dc88976 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,1029 +1,1029 @@ -# Redmine - project management software -# Copyright (C) 2006-2013 Jean-Philippe Lang -# -# 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. - -require "digest/sha1" - -class User < Principal - TEACHER = 0 - STUDENT = 1 - ENTERPRISE = 2 - DEVELOPER = 3 - - include Redmine::SafeAttributes - seems_rateable_rater - # Different ways of displaying/sorting users - USER_FORMATS = { - :firstname_lastname => { - :string => '#{firstname} #{lastname}', - :order => %w(firstname lastname id), - :setting_order => 1 - }, - :firstname_lastinitial => { - :string => '#{firstname} #{lastname.to_s.chars.first}.', - :order => %w(firstname lastname id), - :setting_order => 2 - }, - :firstname => { - :string => '#{firstname}', - :order => %w(firstname id), - :setting_order => 3 - }, - :lastname_firstname => { - :string => '#{lastname} #{firstname}', - :order => %w(lastname firstname id), - :setting_order => 4 - }, - :lastname_coma_firstname => { - :string => '#{lastname}, #{firstname}', - :order => %w(lastname firstname id), - :setting_order => 5 - }, - :lastname => { - :string => '#{lastname}', - :order => %w(lastname id), - :setting_order => 6 - }, - :username => { - :string => '#{login}', - :order => %w(login id), - :setting_order => 7 - }, - } - - MAIL_NOTIFICATION_OPTIONS = [ - ['all', :label_user_mail_option_all], - ['week', :label_user_mail_option_week], - ['day', :label_user_mail_option_day], - ['none', :label_user_mail_option_none] - ] - - has_many :homework_users - has_many :homework_attaches, :through => :homework_users - has_many :homework_evaluations - - #问卷相关关关系 - has_many :poll_users, :dependent => :destroy - has_many :poll_votes, :dependent => :destroy - has_many :poll, :dependent => :destroy #用户创建的问卷 - has_many :answers, :source => :poll, :through => :poll_users, :dependent => :destroy #用户已经完成问答的问卷 - # end - - has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)}, - :after_remove => Proc.new {|user, group| group.user_removed(user)} - has_many :changesets, :dependent => :nullify - has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' - has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'" - has_one :api_token, :class_name => 'Token', :conditions => "action='api'" - belongs_to :auth_source - belongs_to :ucourse, :class_name => 'Course', :foreign_key => :id #huang -## added by xianbo for delete - has_many :biding_projects, :dependent => :destroy - has_many :contesting_projects, :dependent => :destroy - belongs_to :softapplication, :foreign_key => 'id', :dependent => :destroy -##ended by xianbo - -#####fq - has_many :jours, :class_name => 'JournalsForMessage', :dependent => :destroy - has_many :journals_messages, :class_name => 'JournalsForMessage', :foreign_key => "user_id", :dependent => :destroy - has_many :bids, :foreign_key => 'author_id', :dependent => :destroy - has_many :contests, :foreign_key => 'author_id', :dependent => :destroy - has_many :softapplications, :foreign_key => 'user_id', :dependent => :destroy - has_many :journals_for_messages, :as => :jour, :dependent => :destroy - has_many :new_jours, :as => :jour, :class_name => 'JournalsForMessage', :conditions => "status=1" - has_many :journal_replies, :dependent => :destroy - has_many :activities, :dependent => :destroy - has_many :students_for_courses - #has_many :courses, :through => :students_for_courses, :source => :project - has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy - has_many :file_commit, :class_name => 'Attachment', :foreign_key => 'author_id', :conditions => "container_type = 'Project' or container_type = 'Version'" -#### -# added by bai - has_many :join_in_contests, :dependent => :destroy - has_many :news, :foreign_key => 'author_id' - has_many :contestnotification, :foreign_key => 'author_id' - has_many :comments, :foreign_key => 'author_id' - has_many :notificationcomments, :foreign_key => 'author_id' - has_many :wiki_contents, :foreign_key => 'author_id' - has_many :journals - has_many :messages, :foreign_key => 'author_id' - has_one :user_score, :dependent => :destroy - has_many :documents # 项目中关联的文档再次与人关联 -# end - -######added by nie - has_many :project_infos, :dependent => :destroy - has_one :user_status, :dependent => :destroy - ##### - has_many :shares ,:dependent => :destroy - - # add by zjc - has_one :level, :class_name => 'UserLevels', :dependent => :destroy - has_many :memos , :foreign_key => 'author_id' - ##### - scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") } - scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) } - scope :visible, lambda {|*args| - nil - } - - - acts_as_customizable - ############################added by william - acts_as_taggable - scope :by_join_date, order("created_on DESC") - ############################# added by liuping 关注 - acts_as_watchable - - has_one :user_extensions,:dependent => :destroy - ## end - - # default_scope -> { includes(:user_extensions, :user_score) } - scope :teacher, -> { - joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::TEACHER) - } - scope :student, -> { - joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::STUDENT) - } - scope :developer, -> { - joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::DEVELOPER) - } - scope :enterprise, -> { - joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::ENTERPRISE) - } - - attr_accessor :password, :password_confirmation - attr_accessor :last_before_login_on - # Prevents unauthorized assignments - attr_protected :login, :admin, :password, :password_confirmation, :hashed_password - - LOGIN_LENGTH_LIMIT = 25 - MAIL_LENGTH_LIMIT = 60 - - validates_presence_of :login, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } - validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false - validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false - # Login must contain letters, numbers, underscores only - validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i - validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT - validates_length_of :firstname, :maximum => 30 - validates_length_of :lastname, :maximum => 30 - validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true - validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true - validates_confirmation_of :password, :allow_nil => true - validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true - validate :validate_password_length - # validates_email_realness_of :mail - before_create :set_mail_notification - before_save :update_hashed_password - before_destroy :remove_references_before_destroy - # added by fq - after_create :act_as_activity - # end - - scope :in_group, lambda {|group| - group_id = group.is_a?(Group) ? group.id : group.to_i - where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) - } - scope :not_in_group, lambda {|group| - group_id = group.is_a?(Group) ? group.id : group.to_i - where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) - } - scope :sorted, lambda { order(*User.fields_for_order_statement)} - - scope :like, lambda {|arg, type| - if arg.blank? - where(nil) - else - pattern = "%#{arg.to_s.strip.downcase}%" - #where(" LOWER(concat(lastname, firstname)) LIKE :p ", :p => pattern) - if type == "0" - where(" LOWER(login) LIKE '#{pattern}' ") - elsif type == "1" - where(" LOWER(concat(lastname, firstname)) LIKE '#{pattern}' ") - elsif type == "3" - where(" LOWER(concat(lastname, firstname,login)) LIKE '#{pattern}' ") - else - where(" LOWER(mail) LIKE '#{pattern}' ") - end - end - } - - - # ====================================================================== - - def extensions - self.user_extensions ||= UserExtensions.new - end - - def user_score_attr - self.user_score ||= UserScore.new - end - - # ====================================================================== - - #选择项目成员时显示的用户信息文字 - def userInfo - if self.realname.gsub(' ','') == "" || self.realname.nil? - info = self.nickname; - else - info=self.nickname + ' (' + self.realname + ')'; - end - info - end - - ###添加留言 fq - def add_jour(user, notes, reference_user_id = 0, options = {}) - if options.count == 0 - self.journals_for_messages << JournalsForMessage.new(:user_id => user.id, :notes => notes, :reply_id => reference_user_id, :status => true) - else - jfm = self.journals_for_messages.build(options) - jfm.save - jfm - end - end - - # 判断用户是否加入了竞赛中 fq - def join_in_contest?(bid) - joined = JoinInContest.where('user_id = ? and bid_id =?', self.id, bid.id) - if joined.size > 0 - true - else - false - end - end - - ### fq - def join_in?(course) - joined = StudentsForCourse.where('student_id = ? and course_id = ?', self.id, course.id) - if joined.size > 0 - true - else - false - end - end - - def show_name - unless self.user_extensions.nil? - if self.user_extensions.identity == 2 - firstname - else - lastname+firstname - end - else - lastname+firstname - end - end - ## end - - def count_new_jour - count = self.new_jours.count - end - - #added by nie - def count_new_journal_reply - count = self.journal_reply.count - end - - def set_mail_notification - ##add byxianbo - thread=Thread.new do - self.mail_notification = Setting.default_notification_option if self.mail_notification.blank? - true - end - end - - def update_hashed_password - # update hashed_password if password was set - if self.password && self.auth_source_id.blank? - salt_password(password) - end - end - - alias :base_reload :reload - def reload(*args) - @name = nil - @projects_by_role = nil - @courses_by_role = nil - @membership_by_project_id = nil - base_reload(*args) - end - - def mail=(arg) - write_attribute(:mail, arg.to_s.strip) - end - - def identity_url=(url) - if url.blank? - write_attribute(:identity_url, '') - else - begin - write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url)) - rescue OpenIdAuthentication::InvalidOpenId - # Invalid url, don't save - end - end - self.read_attribute(:identity_url) - end - - VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i - # VALID_EMAIL_REGEX = /^[0-9a-zA-Z_-]+@[0-9a-zA-Z_-]+(\.[0-9a-zA-Z_-]+)+$/ - # Returns the user that matches provided login and password, or nil - #登录,返回用户名与密码匹配的用户 - def self.try_to_login(login, password) - login = login.to_s.lstrip.rstrip - password = password.to_s - - # Make sure no one can sign in with an empty login or password - return nil if login.empty? || password.empty? - if (login =~ VALID_EMAIL_REGEX) - user = find_by_mail(login) - else - user = find_by_login(login) - end - if user - # user is already in local database - #return nil unless user.active? - return nil unless user.check_password?(password) - else - # user is not yet registered, try to authenticate with available sources - attrs = AuthSource.authenticate(login, password) - if attrs - user = new(attrs) - user.login = login - user.language = Setting.default_language - if user.save - user.reload - logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source - end - end - end - if user && !user.new_record? - last_login_on = user.last_login_on.nil? ? '' : user.last_login_on.to_s - user.update_column(:last_login_on, Time.now) - end - [user, last_login_on] - rescue => text - raise text - end - - - def self.try_to_autologin(key) - user = Token.find_active_user('autologin', key, Setting.autologin.to_i) - if user - user.update_column(:last_login_on, Time.now) - user - end - end - - def self.name_formatter(formatter = nil) - USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname] - end - - # Returns an array of fields names than can be used to make an order statement for users - # according to how user names are displayed - # Examples: - # - # User.fields_for_order_statement => ['users.login', 'users.id'] - # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id'] - def self.fields_for_order_statement(table=nil) - table ||= table_name - name_formatter[:order].map {|field| "#{table}.#{field}"} - end - - # Return user's full name for display - def realname(formatter = nil) - f = self.class.name_formatter(formatter) - if formatter - eval('"' + f[:string] + '"') - else - @name ||= eval('"' + f[:string] + '"') - end - end - - def nickname(formatter = nil) - login - end - - def name(formatter = nil) - login - end - - def active? - self.status == STATUS_ACTIVE - end - - def registered? - self.status == STATUS_REGISTERED - end - - def locked? - self.status == STATUS_LOCKED - end - - def activate - self.status = STATUS_ACTIVE - end - - def register - self.status = STATUS_REGISTERED - end - - def lock - self.status = STATUS_LOCKED - end - - def activate! - update_attribute(:status, STATUS_ACTIVE) - end - - def register! - update_attribute(:status, STATUS_REGISTERED) - end - - def lock! - update_attribute(:status, STATUS_LOCKED) - end - - # Returns true if +clear_password+ is the correct user's password, otherwise false - def check_password?(clear_password) - if auth_source_id.present? - auth_source.authenticate(self.login, clear_password) - else - User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password - end - end - def check_password1?(clear_password) - - clear_password == hashed_password - - end - # Generates a random salt and computes hashed_password for +clear_password+ - # The hashed password is stored in the following form: SHA1(salt + SHA1(password)) - def salt_password(clear_password) - self.salt = User.generate_salt - self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}") - end - - # Does the backend storage allow this user to change their password? - def change_password_allowed? - return true if auth_source.nil? - return auth_source.allow_password_changes? - end - - # Generate and set a random password. Useful for automated user creation - # Based on Token#generate_token_value - # - def random_password - chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a - password = '' - 40.times { |i| password << chars[rand(chars.size-1)] } - self.password = password - self.password_confirmation = password - self - end - - def pref - self.preference ||= UserPreference.new(:user => self) - end - - def time_zone - @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone]) - end - - def wants_comments_in_reverse_order? - self.pref[:comments_sorting] == 'desc' - end - - def wants_notificationcomments_in_reverse_order? - self.pref[:notificationcomments_sorting] == 'desc' - end - # Return user's RSS key (a 40 chars long string), used to access feeds - def rss_key - if rss_token.nil? - create_rss_token(:action => 'feeds') - end - rss_token.value - end - - # Return user's API key (a 40 chars long string), used to access the API - def api_key - if api_token.nil? - create_api_token(:action => 'api') - end - api_token.value - end - - # Return an array of project ids for which the user has explicitly turned mail notifications on - def notified_projects_ids - @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id) - end - - def notified_project_ids=(ids) - Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id]) - Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty? - @notified_projects_ids = nil - notified_projects_ids - end - - def valid_notification_options - self.class.valid_notification_options(self) - end - - # Only users that belong to more than 1 project can select projects for which they are notified - def self.valid_notification_options(user=nil) - # Note that @user.membership.size would fail since AR ignores - # :include association option when doing a count - if user.nil? || user.memberships.length < 1 - MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'} - else - MAIL_NOTIFICATION_OPTIONS - end - end - - # Find a user account by matching the exact login and then a case-insensitive - # version. Exact matches will be given priority. - #通过用户名查找相应的用户,若没有匹配到,则不区分大小写进行查询 - #修改:不再匹配不区分大小写情况 -zjc - def self.find_by_login(login) - if login.present? - login = login.to_s - # First look for an exact match - user = where(:login => login).all.detect {|u| u.login == login} - #unless user - # # Fail over to case-insensitive if none was found - # user = where("LOWER(login) = ?", login.downcase).first - #end - user - end - end - - def self.find_by_rss_key(key) - Token.find_active_user('feeds', key) - end - - def self.find_by_api_key(key) - Token.find_active_user('api', key) - end - - # Makes find_by_mail case-insensitive - def self.find_by_mail(mail) - where("LOWER(mail) = ?", mail.to_s.downcase).first - end - - # Returns true if the default admin account can no longer be used - def self.default_admin_account_changed? - !User.active.find_by_login("admin").try(:check_password?, "admin") - end - - def to_s - name - end - - CSS_CLASS_BY_STATUS = { - STATUS_ANONYMOUS => 'anon', - STATUS_ACTIVE => 'active', - STATUS_REGISTERED => 'registered', - STATUS_LOCKED => 'locked' - } - - def css_classes - "user #{CSS_CLASS_BY_STATUS[status]}" - end - - # Returns the current day according to user's time zone - def today - if time_zone.nil? - Date.today - else - Time.now.in_time_zone(time_zone).to_date - end - end - - # Returns the day of +time+ according to user's time zone - def time_to_date(time) - if time_zone.nil? - time.to_date - else - time.in_time_zone(time_zone).to_date - end - end - - def logged? - true - end - - def anonymous? - !logged? - end - - # Returns user's membership for the given project - # or nil if the user is not a member of project - def membership(project) - project_id = project.is_a?(Project) ? project.id : project - - @membership_by_project_id ||= Hash.new {|h, project_id| - h[project_id] = memberships.where(:project_id => project_id).first - } - @membership_by_project_id[project_id] - end - - def coursemembership(course) - course_id = course.is_a?(Course) ? course.id : course - - @membership_by_course_id ||= Hash.new {|h, course_id| - h[course_id] = coursememberships.where(:course_id => course_id).first - } - @membership_by_course_id[course_id] - end - - # Return user's roles for project - def roles_for_project(project) - roles = [] - # No role on archived projects - return roles if project.nil? || project.archived? - if logged? - # Find project membership - membership = membership(project) - if membership - roles = membership.roles - else - @role_non_member ||= Role.non_member - roles << @role_non_member - end - else - @role_anonymous ||= Role.anonymous - roles << @role_anonymous - end - roles - end - - # 用户课程权限判断 - def roles_for_course(course) - roles = [] - # No role on archived courses - return roles if course.nil? || course.archived? - if logged? - # Find course membership - membership = coursemembership(course) - if membership - roles = membership.roles - else - @role_non_member ||= Role.non_member - roles << @role_non_member - end - else - @role_anonymous ||= Role.anonymous - roles << @role_anonymous - end - roles - end - - # Return true if the user is a member of project - def member_of?(project) - projects.to_a.include?(project) - end - - def member_of_course?(course) - courses.to_a.include?(course) - end - - def member_of_course_group?(course_group) - course_groups.to_a.include?(course_group) - end - # Returns a hash of user's projects grouped by roles - def projects_by_role - return @projects_by_role if @projects_by_role - - @projects_by_role = Hash.new([]) - memberships.each do |membership| - if membership.project - membership.roles.each do |role| - @projects_by_role[role] = [] unless @projects_by_role.key?(role) - @projects_by_role[role] << membership.project - end - end - end - @projects_by_role.each do |role, projects| - projects.uniq! - end - - @projects_by_role - end - - # 课程的角色权限 - def courses_by_role - return @courses_by_role if @courses_by_role - - @courses_by_role = Hash.new([]) - coursememberships.each do |membership| - if membership.course - membership.roles.each do |role| - @courses_by_role[role] = [] unless @courses_by_role.key?(role) - @courses_by_role[role] << membership.course - end - end - end - @courses_by_role.each do |role, courses| - courses.uniq! - end - - @courses_by_role - end - # Returns true if user is arg or belongs to arg - def is_or_belongs_to?(arg) - if arg.is_a?(User) - self == arg - elsif arg.is_a?(Group) - arg.users.include?(self) - else - false - end - end - - - # Return true if the user is allowed to do the specified action on a specific context - # Action can be: - # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') - # * a permission Symbol (eg. :edit_project) - # Context can be: - # * a project : returns true if user is allowed to do the specified action on this project - # * an array of projects : returns true if user is allowed on every project - # * nil with options[:global] set : check if user has at least one role allowed for this action, - # or falls back to Non Member / Anonymous permissions depending if the user is logged - def allowed_to?(action, context, options={}, &block) - if context && context.is_a?(Project) - return false unless context.allows_to?(action) - # Admin users are authorized for anything else - return true if admin? - - roles = roles_for_project(context) - return false unless roles - roles.any? {|role| - (context.is_public? || role.member?) && - role.allowed_to?(action) && - (block_given? ? yield(role, self) : true) - } - #添加课程相关的权限判断 - elsif context && context.is_a?(Course) - return false unless context.allows_to?(action) - # Admin users are authorized for anything else - return true if admin? - - roles = roles_for_course(context) - return false unless roles - roles.any? {|role| - (context.is_public? || role.member?) && - role.allowed_to?(action) && - (block_given? ? yield(role, self) : true) - } - elsif context && context.is_a?(Array) - if context.empty? - false - else - # Authorize if user is authorized on every element of the array - context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&) - end - elsif options[:global] - # Admin users are always authorized - return true if admin? - - # authorize if user has at least one role that has this permission - roles = memberships.collect {|m| m.roles}.flatten.uniq - if roles.count == 0 - roles = coursememberships.collect {|m| m.roles}.flatten.uniq - end - roles << (self.logged? ? Role.non_member : Role.anonymous) - roles.any? {|role| - role.allowed_to?(action) && - (block_given? ? yield(role, self) : true) - } - else - if admin? - return true - end - #无项目时 查看Non member(id为1)角色是否有权限执行action - Role.find('1').allowed_to?(action) - # false - end - end - - # Is the user allowed to do the specified action on any project? - # See allowed_to? for the actions and valid options. - def allowed_to_globally?(action, options, &block) - allowed_to?(action, nil, options.reverse_merge(:global => true), &block) - end - - # Returns true if the user is allowed to delete his own account - def own_account_deletable? - Setting.unsubscribe? && - (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?) - end - - safe_attributes 'login', - 'firstname', - 'lastname', - 'mail', - 'mail_notification', - 'language', - 'custom_field_values', - 'custom_fields', - 'identity_url' - - safe_attributes 'status', - 'auth_source_id', - :if => lambda {|user, current_user| current_user.admin?} - - safe_attributes 'group_ids', - :if => lambda {|user, current_user| current_user.admin? && !user.new_record?} - - # Utility method to help check if a user should be notified about an - # event. - # - # TODO: only supports Issue events currently - def notify_about?(object) - if mail_notification == 'all' - true - elsif mail_notification.blank? || mail_notification == 'none' - false - else - case object - when Issue - case mail_notification - when 'selected', 'only_my_events' - # user receives notifications for created/assigned issues on unselected projects - object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was) - when 'only_assigned' - is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was) - when 'only_owner' - object.author == self - end - when News - # always send to project members except when mail_notification is set to 'none' - true - #判定用户是否接受留言提醒邮件 - when JournalsForMessage - ##如果是直接留言并且留言对象是Project并且Project类型是课程(课程留言) - if !object.at_user && object.jour.class.to_s.to_sym == :Project && object.jour.project_type == 1 - #根据用户设置邮件接收模式判定当前用户是否接受邮件提醒 - is_notified_project object.jour - end - - end - end - end - - #用户是否接收project的消息提醒 - def is_notified_project arg - if arg.is_a?(Project) - case mail_notification - when 'selected' - notified_projects_ids.include?(arg.id) - when 'only_my_events' - projects.include?(arg) - when 'only_assigned' - false - when 'only_owner' - course = Course.find_by_extra(arg.identifier) - course.teacher == self - end - #勾选的项目或用户的项目 TODO:需改 - #notified_projects_ids.include?(arg) || projects.include?(arg) - else - false - end - end - - def self.current=(user) - Thread.current[:current_user] = user - end - - def self.current - Thread.current[:current_user] ||= User.anonymous - end - - # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only - # one anonymous user per database. - def self.anonymous - anonymous_user = AnonymousUser.first - if anonymous_user.nil? - anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0) - raise 'Unable to create the anonymous user.' if anonymous_user.new_record? - end - anonymous_user - end - - # Salts all existing unsalted passwords - # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password)) - # This method is used in the SaltPasswords migration and is to be kept as is - def self.salt_unsalted_passwords! - transaction do - User.where("salt IS NULL OR salt = ''").find_each do |user| - next if user.hashed_password.blank? - salt = User.generate_salt - hashed_password = User.hash_password("#{salt}#{user.hashed_password}") - User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password) - end - end - end - - protected - - def validate_password_length - # Password length validation based on setting - if !password.nil? && password.size < Setting.password_min_length.to_i - errors.add(:password, :too_short, :count => Setting.password_min_length.to_i) - end - end - private - - def act_as_activity - self.acts << Activity.new(:user_id => self.id) - end - - # Removes references that are not handled by associations - # Things that are not deleted are reassociated with the anonymous user - def remove_references_before_destroy - return if self.id.nil? - - substitute = User.anonymous - Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - Notificationcomment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id] - Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] - JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s] - JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s] - Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - # Remove private queries and keep public ones - ::Query.delete_all ['user_id = ? AND is_public = ?', id, false] - ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] - TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] - Token.delete_all ['user_id = ?', id] - Watcher.delete_all ['user_id = ?', id] - WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] - end - - # Return password digest - def self.hash_password(clear_password) - Digest::SHA1.hexdigest(clear_password || "") - end - - # Returns a 128bits random salt as a hex string (32 chars long) - def self.generate_salt - Redmine::Utils.random_hex(16) - end - - - -end - -class AnonymousUser < User - validate :validate_anonymous_uniqueness, :on => :create - - def validate_anonymous_uniqueness - # There should be only one AnonymousUser in the database - errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists? - end - - def available_custom_fields - [] - end - - # Overrides a few properties - def logged?; false end - def admin; false end - def name(*args); I18n.t(:label_user_anonymous) end - def mail; nil end - def time_zone; nil end - def rss_key; nil end - - def pref - UserPreference.new(:user => self) - end - - # def member_of?(project) - # false - # end - - # Anonymous user can not be destroyed - def destroy - false - end -end +# Redmine - project management software +# Copyright (C) 2006-2013 Jean-Philippe Lang +# +# 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. + +require "digest/sha1" + +class User < Principal + TEACHER = 0 + STUDENT = 1 + ENTERPRISE = 2 + DEVELOPER = 3 + + include Redmine::SafeAttributes + seems_rateable_rater + # Different ways of displaying/sorting users + USER_FORMATS = { + :firstname_lastname => { + :string => '#{firstname} #{lastname}', + :order => %w(firstname lastname id), + :setting_order => 1 + }, + :firstname_lastinitial => { + :string => '#{firstname} #{lastname.to_s.chars.first}.', + :order => %w(firstname lastname id), + :setting_order => 2 + }, + :firstname => { + :string => '#{firstname}', + :order => %w(firstname id), + :setting_order => 3 + }, + :lastname_firstname => { + :string => '#{lastname} #{firstname}', + :order => %w(lastname firstname id), + :setting_order => 4 + }, + :lastname_coma_firstname => { + :string => '#{lastname}, #{firstname}', + :order => %w(lastname firstname id), + :setting_order => 5 + }, + :lastname => { + :string => '#{lastname}', + :order => %w(lastname id), + :setting_order => 6 + }, + :username => { + :string => '#{login}', + :order => %w(login id), + :setting_order => 7 + }, + } + + MAIL_NOTIFICATION_OPTIONS = [ + ['all', :label_user_mail_option_all], + ['week', :label_user_mail_option_week], + ['day', :label_user_mail_option_day], + ['none', :label_user_mail_option_none] + ] + + has_many :homework_users + has_many :homework_attaches, :through => :homework_users + has_many :homework_evaluations + + #问卷相关关关系 + has_many :poll_users, :dependent => :destroy + has_many :poll_votes, :dependent => :destroy + has_many :poll, :dependent => :destroy #用户创建的问卷 + has_many :answers, :source => :poll, :through => :poll_users, :dependent => :destroy #用户已经完成问答的问卷 + # end + + has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)}, + :after_remove => Proc.new {|user, group| group.user_removed(user)} + has_many :changesets, :dependent => :nullify + has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' + has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'" + has_one :api_token, :class_name => 'Token', :conditions => "action='api'" + belongs_to :auth_source + belongs_to :ucourse, :class_name => 'Course', :foreign_key => :id #huang +## added by xianbo for delete + has_many :biding_projects, :dependent => :destroy + has_many :contesting_projects, :dependent => :destroy + belongs_to :softapplication, :foreign_key => 'id', :dependent => :destroy +##ended by xianbo + +#####fq + has_many :jours, :class_name => 'JournalsForMessage', :dependent => :destroy + has_many :journals_messages, :class_name => 'JournalsForMessage', :foreign_key => "user_id", :dependent => :destroy + has_many :bids, :foreign_key => 'author_id', :dependent => :destroy + has_many :contests, :foreign_key => 'author_id', :dependent => :destroy + has_many :softapplications, :foreign_key => 'user_id', :dependent => :destroy + has_many :journals_for_messages, :as => :jour, :dependent => :destroy + has_many :new_jours, :as => :jour, :class_name => 'JournalsForMessage', :conditions => "status=1" + has_many :journal_replies, :dependent => :destroy + has_many :activities, :dependent => :destroy + has_many :students_for_courses + #has_many :courses, :through => :students_for_courses, :source => :project + has_many :acts, :class_name => 'Activity', :as => :act, :dependent => :destroy + has_many :file_commit, :class_name => 'Attachment', :foreign_key => 'author_id', :conditions => "container_type = 'Project' or container_type = 'Version'" +#### +# added by bai + has_many :join_in_contests, :dependent => :destroy + has_many :news, :foreign_key => 'author_id' + has_many :contestnotification, :foreign_key => 'author_id' + has_many :comments, :foreign_key => 'author_id' + has_many :notificationcomments, :foreign_key => 'author_id' + has_many :wiki_contents, :foreign_key => 'author_id' + has_many :journals + has_many :messages, :foreign_key => 'author_id' + has_one :user_score, :dependent => :destroy + has_many :documents # 项目中关联的文档再次与人关联 +# end + +######added by nie + has_many :project_infos, :dependent => :destroy + has_one :user_status, :dependent => :destroy + ##### + has_many :shares ,:dependent => :destroy + + # add by zjc + has_one :level, :class_name => 'UserLevels', :dependent => :destroy + has_many :memos , :foreign_key => 'author_id' + ##### + scope :logged, lambda { where("#{User.table_name}.status <> #{STATUS_ANONYMOUS}") } + scope :status, lambda {|arg| where(arg.blank? ? nil : {:status => arg.to_i}) } + scope :visible, lambda {|*args| + nil + } + + + acts_as_customizable + ############################added by william + acts_as_taggable + scope :by_join_date, order("created_on DESC") + ############################# added by liuping 关注 + acts_as_watchable + + has_one :user_extensions,:dependent => :destroy + ## end + + # default_scope -> { includes(:user_extensions, :user_score) } + scope :teacher, -> { + joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::TEACHER) + } + scope :student, -> { + joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::STUDENT) + } + scope :developer, -> { + joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::DEVELOPER) + } + scope :enterprise, -> { + joins(:user_extensions).where('user_extensions.identity = ?', UserExtensions::ENTERPRISE) + } + + attr_accessor :password, :password_confirmation + attr_accessor :last_before_login_on + # Prevents unauthorized assignments + attr_protected :login, :admin, :password, :password_confirmation, :hashed_password + + LOGIN_LENGTH_LIMIT = 25 + MAIL_LENGTH_LIMIT = 60 + + validates_presence_of :login, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } + validates_uniqueness_of :login, :if => Proc.new { |user| user.login_changed? && user.login.present? }, :case_sensitive => false + validates_uniqueness_of :mail, :if => Proc.new { |user| user.mail_changed? && user.mail.present? }, :case_sensitive => false + # Login must contain letters, numbers, underscores only + validates_format_of :login, :with => /\A[a-z0-9_\-@\.]*\z/i + validates_length_of :login, :maximum => LOGIN_LENGTH_LIMIT + validates_length_of :firstname, :maximum => 30 + validates_length_of :lastname, :maximum => 30 + validates_format_of :mail, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => true + validates_length_of :mail, :maximum => MAIL_LENGTH_LIMIT, :allow_nil => true + validates_confirmation_of :password, :allow_nil => true + validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true + validate :validate_password_length + # validates_email_realness_of :mail + before_create :set_mail_notification + before_save :update_hashed_password + before_destroy :remove_references_before_destroy + # added by fq + after_create :act_as_activity + # end + + scope :in_group, lambda {|group| + group_id = group.is_a?(Group) ? group.id : group.to_i + where("#{User.table_name}.id IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) + } + scope :not_in_group, lambda {|group| + group_id = group.is_a?(Group) ? group.id : group.to_i + where("#{User.table_name}.id NOT IN (SELECT gu.user_id FROM #{table_name_prefix}groups_users#{table_name_suffix} gu WHERE gu.group_id = ?)", group_id) + } + scope :sorted, lambda { order(*User.fields_for_order_statement)} + + scope :like, lambda {|arg, type| + if arg.blank? + where(nil) + else + pattern = "%#{arg.to_s.strip.downcase}%" + #where(" LOWER(concat(lastname, firstname)) LIKE :p ", :p => pattern) + if type == "0" + where(" LOWER(login) LIKE '#{pattern}' ") + elsif type == "1" + where(" LOWER(concat(lastname, firstname)) LIKE '#{pattern}' ") + elsif type == "3" + where(" LOWER(concat(lastname, firstname,login)) LIKE '#{pattern}' ") + else + where(" LOWER(mail) LIKE '#{pattern}' ") + end + end + } + + + # ====================================================================== + + def extensions + self.user_extensions ||= UserExtensions.new + end + + def user_score_attr + self.user_score ||= UserScore.new + end + + # ====================================================================== + + #选择项目成员时显示的用户信息文字 + def userInfo + if self.realname.gsub(' ','') == "" || self.realname.nil? + info = self.nickname; + else + info=self.nickname + ' (' + self.realname + ')'; + end + info + end + + ###添加留言 fq + def add_jour(user, notes, reference_user_id = 0, options = {}) + if options.count == 0 + self.journals_for_messages << JournalsForMessage.new(:user_id => user.id, :notes => notes, :reply_id => reference_user_id, :status => true) + else + jfm = self.journals_for_messages.build(options) + jfm.save + jfm + end + end + + # 判断用户是否加入了竞赛中 fq + def join_in_contest?(bid) + joined = JoinInContest.where('user_id = ? and bid_id =?', self.id, bid.id) + if joined.size > 0 + true + else + false + end + end + + ### fq + def join_in?(course) + joined = StudentsForCourse.where('student_id = ? and course_id = ?', self.id, course.id) + if joined.size > 0 + true + else + false + end + end + + def show_name + unless self.user_extensions.nil? + if self.user_extensions.identity == 2 + firstname + else + lastname+firstname + end + else + lastname+firstname + end + end + ## end + + def count_new_jour + count = self.new_jours.count + end + + #added by nie + def count_new_journal_reply + count = self.journal_reply.count + end + + def set_mail_notification + ##add byxianbo + thread=Thread.new do + self.mail_notification = Setting.default_notification_option if self.mail_notification.blank? + true + end + end + + def update_hashed_password + # update hashed_password if password was set + if self.password && self.auth_source_id.blank? + salt_password(password) + end + end + + alias :base_reload :reload + def reload(*args) + @name = nil + @projects_by_role = nil + @courses_by_role = nil + @membership_by_project_id = nil + base_reload(*args) + end + + def mail=(arg) + write_attribute(:mail, arg.to_s.strip) + end + + def identity_url=(url) + if url.blank? + write_attribute(:identity_url, '') + else + begin + write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url)) + rescue OpenIdAuthentication::InvalidOpenId + # Invalid url, don't save + end + end + self.read_attribute(:identity_url) + end + + VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i + # VALID_EMAIL_REGEX = /^[0-9a-zA-Z_-]+@[0-9a-zA-Z_-]+(\.[0-9a-zA-Z_-]+)+$/ + # Returns the user that matches provided login and password, or nil + #登录,返回用户名与密码匹配的用户 + def self.try_to_login(login, password) + login = login.to_s.lstrip.rstrip + password = password.to_s + + # Make sure no one can sign in with an empty login or password + return nil if login.empty? || password.empty? + if (login =~ VALID_EMAIL_REGEX) + user = find_by_mail(login) + else + user = find_by_login(login) + end + if user + # user is already in local database + #return nil unless user.active? + return nil unless user.check_password?(password) + else + # user is not yet registered, try to authenticate with available sources + attrs = AuthSource.authenticate(login, password) + if attrs + user = new(attrs) + user.login = login + user.language = Setting.default_language + if user.save + user.reload + logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source + end + end + end + if user && !user.new_record? + last_login_on = user.last_login_on.nil? ? '' : user.last_login_on.to_s + user.update_column(:last_login_on, Time.now) + end + [user, last_login_on] + rescue => text + raise text + end + + + def self.try_to_autologin(key) + user = Token.find_active_user('autologin', key, Setting.autologin.to_i) + if user + user.update_column(:last_login_on, Time.now) + user + end + end + + def self.name_formatter(formatter = nil) + USER_FORMATS[formatter || Setting.user_format] || USER_FORMATS[:firstname_lastname] + end + + # Returns an array of fields names than can be used to make an order statement for users + # according to how user names are displayed + # Examples: + # + # User.fields_for_order_statement => ['users.login', 'users.id'] + # User.fields_for_order_statement('authors') => ['authors.login', 'authors.id'] + def self.fields_for_order_statement(table=nil) + table ||= table_name + name_formatter[:order].map {|field| "#{table}.#{field}"} + end + + # Return user's full name for display + def realname(formatter = nil) + f = self.class.name_formatter(formatter) + if formatter + eval('"' + f[:string] + '"') + else + @name ||= eval('"' + f[:string] + '"') + end + end + + def nickname(formatter = nil) + login + end + + def name(formatter = nil) + login + end + + def active? + self.status == STATUS_ACTIVE + end + + def registered? + self.status == STATUS_REGISTERED + end + + def locked? + self.status == STATUS_LOCKED + end + + def activate + self.status = STATUS_ACTIVE + end + + def register + self.status = STATUS_REGISTERED + end + + def lock + self.status = STATUS_LOCKED + end + + def activate! + update_attribute(:status, STATUS_ACTIVE) + end + + def register! + update_attribute(:status, STATUS_REGISTERED) + end + + def lock! + update_attribute(:status, STATUS_LOCKED) + end + + # Returns true if +clear_password+ is the correct user's password, otherwise false + def check_password?(clear_password) + if auth_source_id.present? + auth_source.authenticate(self.login, clear_password) + else + User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password + end + end + def check_password1?(clear_password) + + clear_password == hashed_password + + end + # Generates a random salt and computes hashed_password for +clear_password+ + # The hashed password is stored in the following form: SHA1(salt + SHA1(password)) + def salt_password(clear_password) + self.salt = User.generate_salt + self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}") + end + + # Does the backend storage allow this user to change their password? + def change_password_allowed? + return true if auth_source.nil? + return auth_source.allow_password_changes? + end + + # Generate and set a random password. Useful for automated user creation + # Based on Token#generate_token_value + # + def random_password + chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + password = '' + 40.times { |i| password << chars[rand(chars.size-1)] } + self.password = password + self.password_confirmation = password + self + end + + def pref + self.preference ||= UserPreference.new(:user => self) + end + + def time_zone + @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone]) + end + + def wants_comments_in_reverse_order? + self.pref[:comments_sorting] == 'desc' + end + + def wants_notificationcomments_in_reverse_order? + self.pref[:notificationcomments_sorting] == 'desc' + end + # Return user's RSS key (a 40 chars long string), used to access feeds + def rss_key + if rss_token.nil? + create_rss_token(:action => 'feeds') + end + rss_token.value + end + + # Return user's API key (a 40 chars long string), used to access the API + def api_key + if api_token.nil? + create_api_token(:action => 'api') + end + api_token.value + end + + # Return an array of project ids for which the user has explicitly turned mail notifications on + def notified_projects_ids + @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id) + end + + def notified_project_ids=(ids) + Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id]) + Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty? + @notified_projects_ids = nil + notified_projects_ids + end + + def valid_notification_options + self.class.valid_notification_options(self) + end + + # Only users that belong to more than 1 project can select projects for which they are notified + def self.valid_notification_options(user=nil) + # Note that @user.membership.size would fail since AR ignores + # :include association option when doing a count + if user.nil? || user.memberships.length < 1 + MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'} + else + MAIL_NOTIFICATION_OPTIONS + end + end + + # Find a user account by matching the exact login and then a case-insensitive + # version. Exact matches will be given priority. + #通过用户名查找相应的用户,若没有匹配到,则不区分大小写进行查询 + #修改:不再匹配不区分大小写情况 -zjc + def self.find_by_login(login) + if login.present? + login = login.to_s + # First look for an exact match + user = where(:login => login).all.detect {|u| u.login == login} + #unless user + # # Fail over to case-insensitive if none was found + # user = where("LOWER(login) = ?", login.downcase).first + #end + user + end + end + + def self.find_by_rss_key(key) + Token.find_active_user('feeds', key) + end + + def self.find_by_api_key(key) + Token.find_active_user('api', key) + end + + # Makes find_by_mail case-insensitive + def self.find_by_mail(mail) + where("LOWER(mail) = ?", mail.to_s.downcase).first + end + + # Returns true if the default admin account can no longer be used + def self.default_admin_account_changed? + !User.active.find_by_login("admin").try(:check_password?, "admin") + end + + def to_s + name + end + + CSS_CLASS_BY_STATUS = { + STATUS_ANONYMOUS => 'anon', + STATUS_ACTIVE => 'active', + STATUS_REGISTERED => 'registered', + STATUS_LOCKED => 'locked' + } + + def css_classes + "user #{CSS_CLASS_BY_STATUS[status]}" + end + + # Returns the current day according to user's time zone + def today + if time_zone.nil? + Date.today + else + Time.now.in_time_zone(time_zone).to_date + end + end + + # Returns the day of +time+ according to user's time zone + def time_to_date(time) + if time_zone.nil? + time.to_date + else + time.in_time_zone(time_zone).to_date + end + end + + def logged? + true + end + + def anonymous? + !logged? + end + + # Returns user's membership for the given project + # or nil if the user is not a member of project + def membership(project) + project_id = project.is_a?(Project) ? project.id : project + + @membership_by_project_id ||= Hash.new {|h, project_id| + h[project_id] = memberships.where(:project_id => project_id).first + } + @membership_by_project_id[project_id] + end + + def coursemembership(course) + course_id = course.is_a?(Course) ? course.id : course + + @membership_by_course_id ||= Hash.new {|h, course_id| + h[course_id] = coursememberships.where(:course_id => course_id).first + } + @membership_by_course_id[course_id] + end + + # Return user's roles for project + def roles_for_project(project) + roles = [] + # No role on archived projects + return roles if project.nil? || project.archived? + if logged? + # Find project membership + membership = membership(project) + if membership + roles = membership.roles + else + @role_non_member ||= Role.non_member + roles << @role_non_member + end + else + @role_anonymous ||= Role.anonymous + roles << @role_anonymous + end + roles + end + + # 用户课程权限判断 + def roles_for_course(course) + roles = [] + # No role on archived courses + return roles if course.nil? || course.archived? + if logged? + # Find course membership + membership = coursemembership(course) + if membership + roles = membership.roles + else + @role_non_member ||= Role.non_member + roles << @role_non_member + end + else + @role_anonymous ||= Role.anonymous + roles << @role_anonymous + end + roles + end + + # Return true if the user is a member of project + def member_of?(project) + projects.to_a.include?(project) + end + + def member_of_course?(course) + courses.to_a.include?(course) + end + + def member_of_course_group?(course_group) + course_groups.to_a.include?(course_group) + end + # Returns a hash of user's projects grouped by roles + def projects_by_role + return @projects_by_role if @projects_by_role + + @projects_by_role = Hash.new([]) + memberships.each do |membership| + if membership.project + membership.roles.each do |role| + @projects_by_role[role] = [] unless @projects_by_role.key?(role) + @projects_by_role[role] << membership.project + end + end + end + @projects_by_role.each do |role, projects| + projects.uniq! + end + + @projects_by_role + end + + # 课程的角色权限 + def courses_by_role + return @courses_by_role if @courses_by_role + + @courses_by_role = Hash.new([]) + coursememberships.each do |membership| + if membership.course + membership.roles.each do |role| + @courses_by_role[role] = [] unless @courses_by_role.key?(role) + @courses_by_role[role] << membership.course + end + end + end + @courses_by_role.each do |role, courses| + courses.uniq! + end + + @courses_by_role + end + # Returns true if user is arg or belongs to arg + def is_or_belongs_to?(arg) + if arg.is_a?(User) + self == arg + elsif arg.is_a?(Group) + arg.users.include?(self) + else + false + end + end + + + # Return true if the user is allowed to do the specified action on a specific context + # Action can be: + # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit') + # * a permission Symbol (eg. :edit_project) + # Context can be: + # * a project : returns true if user is allowed to do the specified action on this project + # * an array of projects : returns true if user is allowed on every project + # * nil with options[:global] set : check if user has at least one role allowed for this action, + # or falls back to Non Member / Anonymous permissions depending if the user is logged + def allowed_to?(action, context, options={}, &block) + if context && context.is_a?(Project) + return false unless context.allows_to?(action) + # Admin users are authorized for anything else + return true if admin? + + roles = roles_for_project(context) + return false unless roles + roles.any? {|role| + (context.is_public? || role.member?) && + role.allowed_to?(action) && + (block_given? ? yield(role, self) : true) + } + #添加课程相关的权限判断 + elsif context && context.is_a?(Course) + return false unless context.allows_to?(action) + # Admin users are authorized for anything else + return true if admin? + + roles = roles_for_course(context) + return false unless roles + roles.any? {|role| + (context.is_public? || role.member?) && + role.allowed_to?(action) && + (block_given? ? yield(role, self) : true) + } + elsif context && context.is_a?(Array) + if context.empty? + false + else + # Authorize if user is authorized on every element of the array + context.map {|project| allowed_to?(action, project, options, &block)}.reduce(:&) + end + elsif options[:global] + # Admin users are always authorized + return true if admin? + + # authorize if user has at least one role that has this permission + roles = memberships.collect {|m| m.roles}.flatten.uniq + if roles.count == 0 + roles = coursememberships.collect {|m| m.roles}.flatten.uniq + end + roles << (self.logged? ? Role.non_member : Role.anonymous) + roles.any? {|role| + role.allowed_to?(action) && + (block_given? ? yield(role, self) : true) + } + else + if admin? + return true + end + #无项目时 查看Non member(id为1)角色是否有权限执行action + Role.find('1').allowed_to?(action) + # false + end + end + + # Is the user allowed to do the specified action on any project? + # See allowed_to? for the actions and valid options. + def allowed_to_globally?(action, options, &block) + allowed_to?(action, nil, options.reverse_merge(:global => true), &block) + end + + # Returns true if the user is allowed to delete his own account + def own_account_deletable? + Setting.unsubscribe? && + (!admin? || User.active.where("admin = ? AND id <> ?", true, id).exists?) + end + + safe_attributes 'login', + 'firstname', + 'lastname', + 'mail', + 'mail_notification', + 'language', + 'custom_field_values', + 'custom_fields', + 'identity_url' + + safe_attributes 'status', + 'auth_source_id', + :if => lambda {|user, current_user| current_user.admin?} + + safe_attributes 'group_ids', + :if => lambda {|user, current_user| current_user.admin? && !user.new_record?} + + # Utility method to help check if a user should be notified about an + # event. + # + # TODO: only supports Issue events currently + def notify_about?(object) + if mail_notification == 'all' + true + elsif mail_notification.blank? || mail_notification == 'none' + false + else + case object + when Issue + case mail_notification + when 'selected', 'only_my_events' + # user receives notifications for created/assigned issues on unselected projects + object.author == self || is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was) + when 'only_assigned' + is_or_belongs_to?(object.assigned_to) || is_or_belongs_to?(object.assigned_to_was) + when 'only_owner' + object.author == self + end + when News + # always send to project members except when mail_notification is set to 'none' + true + #判定用户是否接受留言提醒邮件 + when JournalsForMessage + ##如果是直接留言并且留言对象是Project并且Project类型是课程(课程留言) + if !object.at_user && object.jour.class.to_s.to_sym == :Project && object.jour.project_type == 1 + #根据用户设置邮件接收模式判定当前用户是否接受邮件提醒 + is_notified_project object.jour + end + + end + end + end + + #用户是否接收project的消息提醒 + def is_notified_project arg + if arg.is_a?(Project) + case mail_notification + when 'selected' + notified_projects_ids.include?(arg.id) + when 'only_my_events' + projects.include?(arg) + when 'only_assigned' + false + when 'only_owner' + course = Course.find_by_extra(arg.identifier) + course.teacher == self + end + #勾选的项目或用户的项目 TODO:需改 + #notified_projects_ids.include?(arg) || projects.include?(arg) + else + false + end + end + + def self.current=(user) + Thread.current[:current_user] = user + end + + def self.current + Thread.current[:current_user] ||= User.anonymous + end + + # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only + # one anonymous user per database. + def self.anonymous + anonymous_user = AnonymousUser.first + if anonymous_user.nil? + anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0) + raise 'Unable to create the anonymous user.' if anonymous_user.new_record? + end + anonymous_user + end + + # Salts all existing unsalted passwords + # It changes password storage scheme from SHA1(password) to SHA1(salt + SHA1(password)) + # This method is used in the SaltPasswords migration and is to be kept as is + def self.salt_unsalted_passwords! + transaction do + User.where("salt IS NULL OR salt = ''").find_each do |user| + next if user.hashed_password.blank? + salt = User.generate_salt + hashed_password = User.hash_password("#{salt}#{user.hashed_password}") + User.where(:id => user.id).update_all(:salt => salt, :hashed_password => hashed_password) + end + end + end + + protected + + def validate_password_length + # Password length validation based on setting + if !password.nil? && password.size < Setting.password_min_length.to_i + errors.add(:password, :too_short, :count => Setting.password_min_length.to_i) + end + end + private + + def act_as_activity + self.acts << Activity.new(:user_id => self.id) + end + + # Removes references that are not handled by associations + # Things that are not deleted are reassociated with the anonymous user + def remove_references_before_destroy + return if self.id.nil? + + substitute = User.anonymous + Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + Notificationcomment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id] + Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] + JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s] + JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s] + Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + # Remove private queries and keep public ones + ::Query.delete_all ['user_id = ? AND is_public = ?', id, false] + ::Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] + TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id] + Token.delete_all ['user_id = ?', id] + Watcher.delete_all ['user_id = ?', id] + WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id] + end + + # Return password digest + def self.hash_password(clear_password) + Digest::SHA1.hexdigest(clear_password || "") + end + + # Returns a 128bits random salt as a hex string (32 chars long) + def self.generate_salt + Redmine::Utils.random_hex(16) + end + + + +end + +class AnonymousUser < User + validate :validate_anonymous_uniqueness, :on => :create + + def validate_anonymous_uniqueness + # There should be only one AnonymousUser in the database + errors.add :base, 'An anonymous user already exists.' if AnonymousUser.exists? + end + + def available_custom_fields + [] + end + + # Overrides a few properties + def logged?; false end + def admin; false end + def name(*args); I18n.t(:label_user_anonymous) end + def mail; nil end + def time_zone; nil end + def rss_key; nil end + + def pref + UserPreference.new(:user => self) + end + + # def member_of?(project) + # false + # end + + # Anonymous user can not be destroyed + def destroy + false + end +end diff --git a/app/models/user_extensions.rb b/app/models/user_extensions.rb index 09c88e6df..d9a0f520a 100644 --- a/app/models/user_extensions.rb +++ b/app/models/user_extensions.rb @@ -1,86 +1,86 @@ -# encoding: utf-8 -=begin - identity字段含义 - 0 教师教授 - 1 学生 - 2 企业 - 3 开发者 -=end -class UserExtensions < ActiveRecord::Base - validate :school, presence: true - - belongs_to :user - belongs_to :school, :class_name => 'School', :foreign_key => :school_id - attr_accessible :user_id,:birthday,:brief_introduction,:gender,:location,:occupation,:work_experience,:zip_code,:identity, :technical_title,:student_id - TEACHER = 0 - STUDENT = 1 - ENTERPRISE = 2 - DEVELOPER = 3 - #this method was used to update the table user_extensions - def update_user_extensions(birthday=nil,brief_introduction=nil, - gender=nil,location=nil,occupation=nil,work_experience=nil,zip_code=nil) - self.birthday = birthday - self.brief_introduction = brief_introduction - self.gender = gender - self.location = location - self.occupation = occupation - self.work_experience = work_experience - self.zip_code = zip_code - self.save - end - - def get_brief_introduction - return self.brief_introduction - end - - -# added by meng - def show_identity - if User.current.language == 'zh'||User.current.language == '' - case self.identity - when 0 - user_identity = l(:label_account_identity_teacher) - when 1 - user_identity = l(:label_account_identity_student) - when 2 - user_identity = l(:label_account_identity_enterprise) - when 3 - user_identity = l(:label_account_identity_developer) - else - user_identity = '' - end - else - case self.identity - when 0 - user_identity = l(:label_account_identity_teacher) - when 1 - user_identity = l(:label_account_identity_student) - when 2 - user_identity = l(:label_account_identity_enterprise) - when 3 - user_identity = l(:label_account_identity_developer) - else - user_identity = '' - end - end - return user_identity - end -# end - - - def self.introduction(user, message) - unless user.user_extensions.nil? - info = user.user_extensions - info.brief_introduction = message - info.save - else - info = UserExtensions.new - info.user_id = user.id - info.brief_introduction = message - info.save - end - end - - - -end +# encoding: utf-8 +=begin + identity字段含义 + 0 教师教授 + 1 学生 + 2 企业 + 3 开发者 +=end +class UserExtensions < ActiveRecord::Base + validate :school, presence: true + + belongs_to :user + belongs_to :school, :class_name => 'School', :foreign_key => :school_id + attr_accessible :user_id,:birthday,:brief_introduction,:gender,:location,:occupation,:work_experience,:zip_code,:identity, :technical_title,:student_id + TEACHER = 0 + STUDENT = 1 + ENTERPRISE = 2 + DEVELOPER = 3 + #this method was used to update the table user_extensions + def update_user_extensions(birthday=nil,brief_introduction=nil, + gender=nil,location=nil,occupation=nil,work_experience=nil,zip_code=nil) + self.birthday = birthday + self.brief_introduction = brief_introduction + self.gender = gender + self.location = location + self.occupation = occupation + self.work_experience = work_experience + self.zip_code = zip_code + self.save + end + + def get_brief_introduction + return self.brief_introduction + end + + +# added by meng + def show_identity + if User.current.language == 'zh'||User.current.language == '' + case self.identity + when 0 + user_identity = l(:label_account_identity_teacher) + when 1 + user_identity = l(:label_account_identity_student) + when 2 + user_identity = l(:label_account_identity_enterprise) + when 3 + user_identity = l(:label_account_identity_developer) + else + user_identity = '' + end + else + case self.identity + when 0 + user_identity = l(:label_account_identity_teacher) + when 1 + user_identity = l(:label_account_identity_student) + when 2 + user_identity = l(:label_account_identity_enterprise) + when 3 + user_identity = l(:label_account_identity_developer) + else + user_identity = '' + end + end + return user_identity + end +# end + + + def self.introduction(user, message) + unless user.user_extensions.nil? + info = user.user_extensions + info.brief_introduction = message + info.save + else + info = UserExtensions.new + info.user_id = user.id + info.brief_introduction = message + info.save + end + end + + + +end diff --git a/app/views/attachments/destroy.js.erb b/app/views/attachments/destroy.js.erb index 4893d22fe..d2a3a11a1 100644 --- a/app/views/attachments/destroy.js.erb +++ b/app/views/attachments/destroy.js.erb @@ -1,8 +1,8 @@ -$('#attachments_<%= j params[:attachment_id] %>').remove(); -var count=$('#attachments_fields>span').length; -if(count<=0){ - $("#upload_file_count").text(<%= l(:label_no_file_uploaded)%>); - $(".remove_all").remove(); -}else{ - $("#upload_file_count").html("已上传"+""+count+""+"个文件"); +$('#attachments_<%= j params[:attachment_id] %>').remove(); +var count=$('#attachments_fields>span').length; +if(count<=0){ + $("#upload_file_count").text(<%= l(:label_no_file_uploaded)%>); + $(".remove_all").remove(); +}else{ + $("#upload_file_count").html("已上传"+""+count+""+"个文件"); } \ No newline at end of file diff --git a/app/views/bids/_bid_homework_show.html.erb b/app/views/bids/_bid_homework_show.html.erb index 9e086ef1b..3e79c3f1b 100644 --- a/app/views/bids/_bid_homework_show.html.erb +++ b/app/views/bids/_bid_homework_show.html.erb @@ -1,201 +1,201 @@ - - - - -<% if bids.blank? %> -<%#= l(:label_uncommit_homework) %> -暂无作业! -<% else %> -<% bids.each do |bid|%> - - - - - -
- <%= link_to(image_tag(url_to_avatar(bid.author), :class => 'avatar'), user_path(bid.author), :class => "avatar") %> - - - - - - - - - - - - - - - - - - -
- - <%= link_to(bid.author.lastname+bid.author.firstname, user_path(bid.author)) %> - - - <%= l(:label_user_create_project_homework) %> - - - <%= link_to(bid.name, course_for_bid_path(bid), :class => 'bid_path') %> - - - - <% if User.current.logged? && is_cur_course_student(@course) %> - <% cur_user_homework = cur_user_homework_for_bid(bid) %> - - <% if bid.open_anonymous_evaluation == 1 %> - <% case bid.comment_status %> - <% when 0 %> - 未开启匿评 - <% when 1 %> -   匿评中..   - <% when 2 %> -   匿评结束   - <% end %> - <% end%> - - <% if cur_user_homework && cur_user_homework.empty? %> - - <%= link_to l(:label_commit_homework),new_exercise_book_path(bid) %> - - <% else %> - - 已 提 交 - - <% end %> - <% end %> - <% if (User.current.admin?||User.current.allowed_to?(:as_teacher,@course)) %> - <% if bid.open_anonymous_evaluation == 1 && bid.homeworks.count >= 2%> - - <% case bid.comment_status %> - <% when 0 %> - <%= link_to '启动匿评', alert_anonymous_comment_bid_path(bid), id: "#{bid.id}_start_anonymous_comment", remote: true, disable_with: '加载中...' %> - <% when 1 %> - <%= link_to '关闭匿评', alert_anonymous_comment_bid_path(bid), id: "#{bid.id}_stop_anonymous_comment", remote: true %> - <% when 2 %> - 匿评结束 - <% end %> - - <%end%> - - <%= link_to( - l(:button_edit), - {:action => 'edit', :controller=>'bids', :course_id =>@course.id, :bid_id => bid.id} - ) %> - - <%#= link_to( - l(:button_delete), - {:action => 'homework_destroy', :controller=>'bids', :course_id => bid.id}, - :method => :post, - :data => {:confirm => l(:text_are_you_sure)}, - :class => 'icon icon-del' - ) %> - <% end %> - -
- - <% bidding_project = bid.biding_projects.all - temp = [] - bidding_project.each do |pro| - if pro.project && pro.project.project_status - temp << pro - end - temp - end - %> - <% if bid.homework_type == 1%> - <%= l(:label_x_homework_project, :count => bid.homeworks.count) %> - ( - - <%= link_to bid.homeworks.count, course_for_bid_path(bid.id) %> - ) - <% else %> - <%= l(:label_x_homework_project, :count => temp.count) %> - ( - - <%= link_to temp.count, course_for_bid_path(bid.id) %> - ) - <% end %> - -
- <% if bid.reward_type.nil? or bid.reward_type == 1 %> - - <%= l(:label_bids_reward_method) %> - - <%= l(:label_call_bonus) %> -   - <%= l(:label_RMB_sign) %> - <%= bid.budget%> - - - <% elsif bid.reward_type == 2 %> - - <%= l(:label_bids_reward_method) %> - - <%= bid.budget%> - - - <% end %> -
- - <%=h sanitize(bid.description.html_safe) %> - -
- - <%= l(:label_end_time) %> - :  - <%= bid.deadline %> - - - <% if betweentime(bid.deadline) < 0 %> - - <%= l(:label_commit_limit)%> - - <% else %> - - - - <% end %> - -
-<% end %> -<% end %> - diff --git a/app/views/boards/show.html.erb b/app/views/boards/show.html.erb index fefe54b78..95c1a76d7 100644 --- a/app/views/boards/show.html.erb +++ b/app/views/boards/show.html.erb @@ -23,7 +23,24 @@