mirror of
https://github.com/ENSL/ensl.org.git
synced 2025-05-31 00:41:20 +00:00
Purged git history and removed sensitive information.
This commit is contained in:
commit
6bcc8dc76b
862 changed files with 25312 additions and 0 deletions
130
app/models/article.rb
Normal file
130
app/models/article.rb
Normal file
|
@ -0,0 +1,130 @@
|
|||
require File.join(Rails.root, 'vendor', 'plugins', 'has_view_count', 'init.rb')
|
||||
require 'rbbcode'
|
||||
|
||||
class Article < ActiveRecord::Base
|
||||
include Exceptions
|
||||
include Extra
|
||||
|
||||
STATUS_PUBLISHED = 0
|
||||
STATUS_DRAFT = 1
|
||||
|
||||
RULES = 400
|
||||
HISTORY = 401
|
||||
HOF = 402
|
||||
SPONSORS = 403
|
||||
LINKS = 404
|
||||
COMPETITIVE = 405
|
||||
PLUGIN = 426
|
||||
EXTRA = 428
|
||||
SB_RULES = 450
|
||||
G_RULES = 464
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :user_id, :version
|
||||
|
||||
scope :recent, :order => "created_at DESC", :limit => 8
|
||||
scope :with_comments,
|
||||
:select => "articles.*, COUNT(C.id) AS comment_num",
|
||||
:joins => "LEFT JOIN comments C ON C.commentable_type = 'Article' AND C.commentable_id = articles.id",
|
||||
:group => "articles.id"
|
||||
scope :ordered, :order => "articles.created_at DESC"
|
||||
scope :limited, :limit => 5
|
||||
scope :nodrafts, :conditions => {:status => STATUS_PUBLISHED}
|
||||
scope :drafts, :conditions => {:status => STATUS_DRAFT}
|
||||
scope :articles, :conditions => ["category_id IN (SELECT id FROM categories WHERE domain = ?)", Category::DOMAIN_ARTICLES]
|
||||
scope :onlynews, :conditions => ["category_id IN (SELECT id FROM categories WHERE domain = ?)", Category::DOMAIN_NEWS]
|
||||
scope :category, lambda { |cat| {:conditions => {:category_id => cat}} }
|
||||
scope :domain, lambda { |domain| {:include => "category", :conditions => {"categories.domain" => domain}} }
|
||||
scope :nospecial, :conditions => ["category_id != ?", Category::SPECIAL]
|
||||
scope :interviews, :conditions => ["category_id = ?", Category::INTERVIEWS]
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :category
|
||||
has_many :comments, :as => :commentable, :order => "created_at ASC", :dependent => :destroy
|
||||
has_many :files, :class_name => "DataFile", :order => "created_at DESC", :dependent => :destroy
|
||||
|
||||
validates_length_of :title, :in => 1..50
|
||||
validates_length_of :text, :in => 1..64000
|
||||
validates_presence_of :user, :category
|
||||
validate :validate_status
|
||||
|
||||
before_validation :init_variables, :if => Proc.new{ |model| model.new_record? }
|
||||
before_save :format_text
|
||||
after_save :send_notifications
|
||||
after_destroy :remove_readings
|
||||
|
||||
has_view_count
|
||||
acts_as_readable
|
||||
acts_as_versioned
|
||||
|
||||
non_versioned_columns << 'category_id'
|
||||
non_versioned_columns << 'status'
|
||||
non_versioned_columns << 'user_id'
|
||||
|
||||
def to_s
|
||||
title
|
||||
end
|
||||
|
||||
def previous_article
|
||||
category.articles.nodrafts.first(:conditions => ["id < ?", self.id], :order => "id DESC")
|
||||
end
|
||||
|
||||
def next_article
|
||||
category.articles.nodrafts.first(:conditions => ["id > ?", self.id], :order => "id ASC")
|
||||
end
|
||||
|
||||
def statuses
|
||||
{STATUS_PUBLISHED => "Published", STATUS_DRAFT => "Draft"}
|
||||
end
|
||||
|
||||
def validate_status
|
||||
errors.add :status, I18n.t(:invalid_status) unless statuses.include? status
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.status = STATUS_DRAFT unless user.admin?
|
||||
self.text_coding = CODING_BBCODE if !user.admin? and text_coding = CODING_HTML
|
||||
end
|
||||
|
||||
def format_text
|
||||
if text_coding == CODING_BBCODE
|
||||
self.text_parsed = RbbCode::Parser.new.parse(text)
|
||||
elsif text_coding == CODING_MARKDOWN
|
||||
self.text_parsed = BlueCloth.new(text).to_html
|
||||
end
|
||||
end
|
||||
|
||||
def send_notifications
|
||||
if (new_record? or status_changed?) and status == STATUS_PUBLISHED
|
||||
case category.domain
|
||||
when Category::DOMAIN_NEWS
|
||||
Profile.all(:include => :user, :conditions => "notify_news = 1").each do |p|
|
||||
Notifications.news p.user, self if p.user
|
||||
end
|
||||
when Category::DOMAIN_ARTICLES
|
||||
Profile.all(:include => :user, :conditions => "notify_articles = 1").each do |p|
|
||||
Notifications.article p.user, self if p.user
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_readings
|
||||
Reading.delete_all ["readable_type = 'Category' AND readable_id = ?", category_id]
|
||||
end
|
||||
|
||||
def can_show? cuser
|
||||
status != STATUS_DRAFT or (cuser and (user == cuser or cuser.admin?))
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and !cuser.banned?(Ban::TYPE_MUTE)
|
||||
end
|
||||
|
||||
def can_update? cuser, params = {}
|
||||
cuser and !cuser.banned?(Ban::TYPE_MUTE) and (cuser.admin? or (user == cuser and !params.keys.include? "status"))
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
88
app/models/ban.rb
Normal file
88
app/models/ban.rb
Normal file
|
@ -0,0 +1,88 @@
|
|||
class Ban < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
TYPE_SITE = 0
|
||||
TYPE_MUTE = 1
|
||||
TYPE_LEAGUE = 2
|
||||
TYPE_SERVER = 3
|
||||
TYPE_VENT = 4
|
||||
TYPE_GATHER = 5
|
||||
VENT_BANS = "tmp/bans.txt"
|
||||
|
||||
attr_protected :id, :created_at, :updated_at
|
||||
attr_accessor :ts, :sign, :len, :user_name
|
||||
|
||||
scope :ordered, :order => "created_at DESC"
|
||||
scope :effective, :conditions => "expiry > UTC_TIMESTAMP()"
|
||||
scope :ineffective, :conditions => "expiry < UTC_TIMESTAMP()"
|
||||
|
||||
validate :validate_ts
|
||||
validate :validate_type
|
||||
validate :validate_ventban
|
||||
validates_format_of :steamid, :with => /\A([0-9]{1,10}:){2}[0-9]{1,10}\Z/, :allow_blank => true
|
||||
validates_format_of :addr, :with => /\A([0-9]{1,3}\.){3}[0-9]{1,3}:?[0-9]{0,5}\z/, :allow_blank => true
|
||||
validates_length_of :reason, :maximum => 255, :allow_nil => true, :allow_blank => true
|
||||
|
||||
before_validation :check_user
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :server
|
||||
|
||||
def color
|
||||
expiry.past? ? "green" : "red"
|
||||
end
|
||||
|
||||
def types
|
||||
{TYPE_SITE => "Website Logon",
|
||||
TYPE_MUTE => "Commenting",
|
||||
TYPE_LEAGUE => "Contests",
|
||||
TYPE_SERVER => "NS Servers",
|
||||
TYPE_VENT => "Ventrilo",
|
||||
TYPE_GATHER => "Gather"}
|
||||
end
|
||||
|
||||
def validate_ts
|
||||
if ts and Verification.verify(steamid + ts.to_s) != sign
|
||||
errors.add :ts, I18n.t(:wrong_verification_code)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_type
|
||||
errors.add :ban_type, I18n.t(:invalid_ban_type) unless types.include? ban_type
|
||||
end
|
||||
|
||||
def validate_ventban
|
||||
if ban_type == TYPE_VENT and !ip.match(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/)
|
||||
errors.add :ip, I18n.t(:ventrilos_ip_ban)
|
||||
end
|
||||
end
|
||||
|
||||
def check_user
|
||||
if user_name
|
||||
self.user = User.find_by_username(user_name)
|
||||
else
|
||||
self.user = User.first(:conditions => {:steamid => steamid})
|
||||
self.server = Server.first(:conditions => ["CONCAT(ip, ':', port) = ?", addr])
|
||||
end
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def self.refresh
|
||||
#file = File.new(VENT_BANS, "w")
|
||||
#Ban.all(:conditions => ["ban_type = ? AND expiry > UTC_TIMESTAMP()", TYPE_VENT]).each do |ban|
|
||||
# file.write "#{ban.ip},,,"
|
||||
#end
|
||||
#file.close
|
||||
end
|
||||
end
|
69
app/models/bracket.rb
Normal file
69
app/models/bracket.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
class Bracket < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :created_at, :updated_at
|
||||
|
||||
belongs_to :contest
|
||||
has_many :bracketers
|
||||
|
||||
def to_s
|
||||
"#" + self.id.to_s
|
||||
end
|
||||
|
||||
def get_bracketer row, col
|
||||
b = bracketers.pos(row, col).first
|
||||
unless b
|
||||
b = bracketers.build
|
||||
b.row = row.to_i
|
||||
b.column = col.to_i
|
||||
b.save
|
||||
end
|
||||
return b
|
||||
end
|
||||
|
||||
def options
|
||||
["-- Matches"] \
|
||||
+ contest.matches.collect{|c| [c, "match_#{c.id}"]} \
|
||||
+ ["-- Teams"] \
|
||||
+ contest.contesters.collect{|c| [c, "contester_#{c.id}"]}
|
||||
end
|
||||
|
||||
def default row, col
|
||||
if b = bracketers.pos(row, col).first
|
||||
if b.match
|
||||
return "match_#{b.match_id}"
|
||||
elsif b.contester
|
||||
return "contester_#{b.team_id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_cells params
|
||||
params.each do |row, cols|
|
||||
cols.each do |col, val|
|
||||
unless val.include? "--"
|
||||
b = get_bracketer(row, col)
|
||||
if m = val.match(/match_(\d*)/)
|
||||
b.update_attribute :match_id, m[1].to_i
|
||||
b.update_attribute :team_id, nil
|
||||
elsif m = val.match(/contester_(\d*)/)
|
||||
b.update_attribute :team_id, m[1].to_i
|
||||
b.update_attribute :match_id, nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
25
app/models/bracketer.rb
Normal file
25
app/models/bracketer.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class Bracketer < ActiveRecord::Base
|
||||
include Exceptions
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
|
||||
belongs_to :contest
|
||||
belongs_to :match
|
||||
belongs_to :contester, :foreign_key => "team_id"
|
||||
|
||||
scope :pos, lambda { |row, col| {:conditions => {"row" => row, "column" => col}} }
|
||||
|
||||
def to_s
|
||||
if self.match_id
|
||||
if match.match_time.past? and (match.score1 and match.score2)
|
||||
winner = match.score1 > match.score2 ? match.contester1.team : match.contester2.team
|
||||
return "#{self.match.score1} - #{self.match.score2}"
|
||||
else
|
||||
return self.match.match_time.strftime("%H:%M %d/%b")
|
||||
end
|
||||
elsif self.contester
|
||||
return self.contester.to_s[0, 10]
|
||||
end
|
||||
end
|
||||
end
|
70
app/models/category.rb
Normal file
70
app/models/category.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
class Category < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
MAIN = 1
|
||||
SPECIAL = 10
|
||||
INTERVIEWS = 11
|
||||
RULES = 36
|
||||
|
||||
DOMAIN_NEWS = 0
|
||||
DOMAIN_ARTICLES = 1
|
||||
DOMAIN_ISSUES = 2
|
||||
DOMAIN_SITES = 3
|
||||
DOMAIN_FORUMS = 4
|
||||
DOMAIN_MOVIES = 5
|
||||
DOMAIN_GAMES = 6
|
||||
|
||||
PER_PAGE = 3
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :sort
|
||||
|
||||
validates_length_of :name, :in => 1..30
|
||||
validate :validate_domain
|
||||
|
||||
scope :ordered, :order => "sort ASC, created_at DESC"
|
||||
scope :domain, lambda { |domain| {:conditions => {:domain => domain}} }
|
||||
scope :nospecial, :conditions => ["name != 'Special'"]
|
||||
scope :newest, :include => :articles, :order => "articles.created_at DESC"
|
||||
scope :page, lambda { |page| {:limit => "#{(page-1)*PER_PAGE}, #{(page-1)*PER_PAGE+PER_PAGE}"} }
|
||||
scope :of_user, lambda { |user| {:conditions => {"articles.user_id" => user.id}, :include => :articles} }
|
||||
|
||||
has_many :articles, :order => "created_at DESC"
|
||||
has_many :issues, :order => "created_at DESC"
|
||||
has_many :forums, :order => "forums.position"
|
||||
has_many :movies
|
||||
has_many :maps
|
||||
has_many :gathers
|
||||
has_many :servers
|
||||
|
||||
acts_as_readable
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def domains
|
||||
{DOMAIN_NEWS => 'News',
|
||||
DOMAIN_ARTICLES => 'Articles',
|
||||
DOMAIN_ISSUES => 'Issues',
|
||||
DOMAIN_SITES => "Sites",
|
||||
DOMAIN_FORUMS => "Forums",
|
||||
DOMAIN_MOVIES => "Movies",
|
||||
DOMAIN_GAMES => "Games"}
|
||||
end
|
||||
|
||||
def validate_domain
|
||||
errors.add :domain, I18n.t(:invalid_domain) unless domains.include? domain
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
239
app/models/challenge.rb
Normal file
239
app/models/challenge.rb
Normal file
|
@ -0,0 +1,239 @@
|
|||
class Challenge < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
STATUS_PENDING = 0
|
||||
STATUS_ACCEPTED = 1
|
||||
STATUS_DEFAULT = 2
|
||||
STATUS_FORFEIT = 3
|
||||
STATUS_DECLINED = 4
|
||||
AUTO_DEFAULT_TIME = 10800 # Normal default time: 3 hours
|
||||
CHALLENGE_BEFORE_MANDATORY = 432000 # Min. time threshold for mandatory matches: 5 days
|
||||
CHALLENGE_BEFORE_VOLUNTARY = 900 # Min. time threshold for voluntary matches: 15 mins
|
||||
ACCEPT_BEFORE_MANDATORY = 86400 # Time to accept before mandatory match time: 1 day
|
||||
ACCEPT_BEFORE_VOLUNTARY = 300 # Time to accept before voluntary match time: 5 mins
|
||||
MATCH_LENGTH = 7200 # Usual match length (for servers): 2 hours
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :default_time, :user_id, :status
|
||||
|
||||
validates_presence_of :contester1, :contester2, :server, :map1
|
||||
validates_presence_of :map2, :on => :update
|
||||
#validates_datetime :match_time, :default_time
|
||||
#validates_length_of [:details, :response], :maximum => 255, :allow_blank => true, :allow_nil => true
|
||||
#validate_on_create:validate_teams
|
||||
#validate_on_create:validate_contest
|
||||
#validate_on_create:validate_mandatory
|
||||
#validate_on_create:validate_match_time
|
||||
#validate_on_create:validate_server
|
||||
#validate_on_create:validate_map1
|
||||
#validate_on_update :validate_map2
|
||||
#validate_on_update :validate_status
|
||||
|
||||
scope :of_contester,
|
||||
lambda { |contester| {:conditions => ["contester1_id = ? OR contester2_id = ?", contester.id, contester.id]} }
|
||||
scope :within_time,
|
||||
lambda { |from, to| {:conditions => ["match_time > ? AND match_time < ?", from.utc, to.utc]} }
|
||||
scope :around,
|
||||
lambda { |time| {:conditions => ["match_time > ? AND match_time < ?", time.ago(MATCH_LENGTH).utc, time.ago(-MATCH_LENGTH).utc]} }
|
||||
scope :on_week,
|
||||
lambda { |time| {:conditions => ["match_time > ? and match_time < ?", time.beginning_of_week, time.end_of_week]} }
|
||||
scope :pending, :conditions => {:status => STATUS_PENDING}
|
||||
scope :mandatory, :conditions => {:mandatory => true}
|
||||
scope :future, :conditions => "match_time > UTC_TIMESTAMP()"
|
||||
scope :past, :conditions => "match_time < UTC_TIMESTAMP()"
|
||||
|
||||
has_one :match
|
||||
belongs_to :map1, :class_name => "Map"
|
||||
belongs_to :map2, :class_name => "Map"
|
||||
belongs_to :user
|
||||
belongs_to :server
|
||||
belongs_to :contester1, :class_name => "Contester", :include => 'team'
|
||||
belongs_to :contester2, :class_name => "Contester", :include => 'team'
|
||||
|
||||
def statuses
|
||||
{STATUS_PENDING => "Pending response",
|
||||
STATUS_ACCEPTED => "Accepted",
|
||||
STATUS_DEFAULT => "Default Time",
|
||||
STATUS_FORFEIT => "Forfeited",
|
||||
STATUS_DECLINED => "Declined"}
|
||||
end
|
||||
|
||||
def autodefault
|
||||
match_time - (mandatory ? ACCEPT_BEFORE_MANDATORY : ACCEPT_BEFORE_VOLUNTARY)
|
||||
end
|
||||
|
||||
def get_margin
|
||||
mandatory ? CHALLENGE_BEFORE_MANDATORY : CHALLENGE_BEFORE_VOLUNTARY
|
||||
end
|
||||
|
||||
def get_deadline
|
||||
mandatory ? ACCEPT_BEFORE_MANDATORY : ACCEPT_BEFORE_VOLUNTARY
|
||||
end
|
||||
|
||||
def get_contester1
|
||||
self.contester1 = user.active_contesters.of_contest(contester2.contest).first
|
||||
end
|
||||
|
||||
def before_validation_on_create
|
||||
self.status = STATUS_PENDING
|
||||
self.default_time = match_time.end_of_week.change \
|
||||
:hour => contester1.contest.default_time.hour,
|
||||
:minute => contester1.contest.default_time.strftime("%M").to_i
|
||||
end
|
||||
|
||||
def after_create
|
||||
contester2.team.teamers.active.leaders.each do |teamer|
|
||||
if teamer.user.profile.notify_pms
|
||||
Notifications.challenge teamer.user, self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_teams
|
||||
if contester1.team == contester2.team
|
||||
errors.add_to_base I18n.t(:challenges_yourself)
|
||||
end
|
||||
if contester1.contest != contester2.contest
|
||||
errors.add_to_base I18n.t(:challenges_opponent_contest)
|
||||
end
|
||||
if !contester2.active or !contester2.team.active
|
||||
errors.add_to_base I18n.t(:challenges_opponent_inactive)
|
||||
end
|
||||
if !contester1.active or !contester1.team.active
|
||||
errors.add_to_base I18n.t(:challenges_inactive)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_contest
|
||||
if contester1.contest.end.past? or contester1.contest.status == Contest::STATUS_CLOSED
|
||||
errors.add_to_base I18n.t(:contests_closed)
|
||||
end
|
||||
if contester1.contest.contest_type != Contest::TYPE_LADDER and !match
|
||||
errors.add_to_base I18n.t(:contests_notladder)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_mandatory
|
||||
return unless mandatory
|
||||
|
||||
if contester2.score < contester1.score
|
||||
errors.add_to_base I18n.t(:challenges_mandatory)
|
||||
end
|
||||
if Challenge.pending.count(:conditions => \
|
||||
["contester1_id = ? AND contester2_id = ? AND mandatory = true AND default_time < UTC_TIMESTAMP()",
|
||||
contester1.id, contester2.id]) > 0
|
||||
errors.add_to_base I18n.t(:challenges_mandatory_handled)
|
||||
end
|
||||
if Match.of_contester(contester2).on_week(match_time).count > 0
|
||||
errors.add_to_base I18n.t(:challenges_opponent_week)
|
||||
end
|
||||
if Challenge.of_contester(contester2).mandatory.on_week(match_time).count > 0
|
||||
errors.add_to_base I18n.t(:challenges_opponent_mandatory_week)
|
||||
end
|
||||
if Challenge.of_contester(contester2).mandatory.on_week(default_time).count > 0
|
||||
errors.add_to_base I18n.t(:challenges_opponent_mandatory_week_defaulttime)
|
||||
end
|
||||
if Match.of_contester(contester2).around(default_time).count > 0
|
||||
errors.add_to_base I18n.t(:challenges_opponent_defaulttime)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_match_time
|
||||
if (match_time-get_margin).past?
|
||||
if get_margin > 86400
|
||||
errors.add_to_base I18n.t(:matches_time1) + get_margin / 60 / 60 / 24 + I18n.t(:matches_time2)
|
||||
else
|
||||
errors.add_to_base I18n.t(:matches_time1) + get_margin / 60 + I18n.t(:matches_time3)
|
||||
end
|
||||
end
|
||||
if Challenge.of_contester(contester2).around(match_time).pending.count > 0
|
||||
errors.add_to_base I18n.t(:challenges_opponent_specifictime)
|
||||
end
|
||||
if Match.of_contester(contester2).around(match_time).count > 0
|
||||
errors.add_to_base I18n.t(:challenges_opponent_match_specifictime)
|
||||
end
|
||||
if match_time > contester1.contest.end
|
||||
errors.add_to_base I18n.t(:contests_end)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_server
|
||||
unless server and server.official
|
||||
errors.add_to_base I18n.t(:servers_notavailable)
|
||||
end
|
||||
unless server.is_free match_time
|
||||
errors.add_to_base I18n.t(:servers_notfree_specifictime)
|
||||
end
|
||||
if !server.is_free default_time
|
||||
errors.add_to_base I18n.t(:servers_notfree_defaulttime)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_map1
|
||||
unless contester1.contest.maps.exists?(map1)
|
||||
errors.add_to_base I18n.t(:contests_map_notavailable)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_map2
|
||||
unless contester2.contest.maps.exists?(map2)
|
||||
errors.add_to_base I18n.t(:contests_map_notavailable)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_status
|
||||
if mandatory and ![STATUS_ACCEPTED, STATUS_DEFAULT, STATUS_FORFEIT].include? status
|
||||
errors.add_to_base I18n.t(:challenges_mandatory_invalidresult)
|
||||
end
|
||||
unless statuses.include? status
|
||||
errors.add_to_base I18n.t(:challenges_mandatory_invalidresult)
|
||||
end
|
||||
end
|
||||
|
||||
def after_update
|
||||
if status_changed?
|
||||
if status == STATUS_ACCEPTED
|
||||
make_match.save
|
||||
elsif status == STATUS_DEFAULT
|
||||
m = make_match
|
||||
m.match_time = default_time
|
||||
m.save
|
||||
elsif status == STATUS_FORFEIT
|
||||
m = make_match
|
||||
m.forfeit = true
|
||||
m.score1 = 4
|
||||
m.score2 = 0
|
||||
m.match_time = default_time
|
||||
m.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_match
|
||||
match = Match.new
|
||||
match.contester1 = contester1
|
||||
match.contester2 = contester2
|
||||
match.map1 = map1
|
||||
match.map2 = map2
|
||||
match.contest = contester1.contest
|
||||
match.challenge = self
|
||||
match.server = server
|
||||
match.match_time = match_time
|
||||
match
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
return false unless cuser
|
||||
return false if cuser.banned?(Ban::TYPE_LEAGUE)
|
||||
validate_teams
|
||||
validate_contest
|
||||
true if (contester1.team.is_leader?(cuser) or cuser.admin?) and errors.size == 0
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and (contester2.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING and autodefault.future?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and (contester1.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING and autodefault.future?
|
||||
end
|
||||
end
|
50
app/models/comment.rb
Normal file
50
app/models/comment.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
require 'rbbcode'
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :user_id
|
||||
|
||||
scope :with_userteam, :include => {:user => :team}
|
||||
scope :recent, :order => "id DESC", :limit => 10
|
||||
scope :recent3, :order => "id DESC", :limit => 3
|
||||
scope :recent5, :order => "id DESC", :limit => 5, :group => "commentable_id, commentable_type"
|
||||
scope :filtered, :conditions => ["commentable_type != 'Issue'"]
|
||||
scope :ordered, :order => "id ASC"
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :commentable, :polymorphic => true
|
||||
|
||||
validates_presence_of :commentable, :user
|
||||
validates_length_of :text, :in => 1..10000
|
||||
|
||||
before_save :parse_text
|
||||
|
||||
# acts_as_indexed :fields => [:text]
|
||||
|
||||
def parse_text
|
||||
self.text_parsed = RbbCode::Parser.new.parse(text)
|
||||
end
|
||||
|
||||
def after_create
|
||||
# if commentable_type == "Movie" or commentable_type == "Article" and commentable.user and commentable.user.profile.notify_own_stuff
|
||||
# Notifications.deliver_comments commentable.user, commentable
|
||||
# end
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
return false unless cuser
|
||||
#errors.add_to_base I18n.t(:comments_locked) if !commentable.lock.nil? and commentable.lock
|
||||
errors.add_to_base I18n.t(:bans_mute) if cuser.banned? Ban::TYPE_MUTE
|
||||
errors.add_to_base I18n.t(:registered_for_week) unless cuser.verified?
|
||||
return errors.count == 0
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and user == cuser or cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
114
app/models/contest.rb
Normal file
114
app/models/contest.rb
Normal file
|
@ -0,0 +1,114 @@
|
|||
class Contest < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
WEIGHT = 30.0
|
||||
STATUS_OPEN = 0
|
||||
STATUS_PROGRESS = 1
|
||||
STATUS_CLOSED = 3
|
||||
TYPE_LADDER = 0
|
||||
TYPE_LEAGUE = 1
|
||||
TYPE_BRACKET = 2
|
||||
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
|
||||
scope :active, :conditions => ["status != ?", STATUS_CLOSED]
|
||||
scope :inactive, :conditions => {:status => STATUS_CLOSED}
|
||||
scope :joinable, :conditions => {:status => STATUS_OPEN}
|
||||
scope :with_contesters, :include => :contesters
|
||||
scope :ordered, :order => "start DESC"
|
||||
scope :nsls1, :conditions => ["name LIKE ?", "NSL S1:%"]
|
||||
scope :nsls2, :conditions => ["name LIKE ?", "NSL S2:%"]
|
||||
scope :ns1seasons, :conditions => ["name LIKE ?", "S%:%"]
|
||||
|
||||
has_many :matches, :dependent => :destroy
|
||||
has_many :weeks, :dependent => :destroy
|
||||
has_many :contesters, :dependent => :destroy, :include => :team
|
||||
has_many :predictions, :through => :matches
|
||||
has_many :brackets
|
||||
has_many :preds_with_score,
|
||||
:source => :predictions,
|
||||
:through => :matches,
|
||||
:select => "predictions.id, predictions.user_id,
|
||||
SUM(result) AS correct,
|
||||
SUM(result)/COUNT(*)*100 AS score,
|
||||
COUNT(*) AS total",
|
||||
:conditions => "result IS NOT NULL",
|
||||
:group => "predictions.user_id",
|
||||
:order => "correct DESC"
|
||||
has_and_belongs_to_many :maps
|
||||
belongs_to :demos, :class_name => "Directory"
|
||||
belongs_to :winner, :class_name => "Contester"
|
||||
belongs_to :rules, :class_name => "Article"
|
||||
|
||||
validates_presence_of :name, :start, :end, :status, :default_time
|
||||
validates_length_of :name, :in => 1..50
|
||||
validates_length_of :short_name, :in => 1..8, :allow_nil => true
|
||||
validate :validate_status
|
||||
validate :validate_contest_type
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def status_s
|
||||
statuses[status]
|
||||
end
|
||||
|
||||
def default_s
|
||||
"Sunday " + default_time.to_s
|
||||
end
|
||||
|
||||
def statuses
|
||||
{STATUS_OPEN => "In Progress (signups open)", STATUS_PROGRESS => "In Progress (signups closed)", STATUS_CLOSED => "Closed"}
|
||||
end
|
||||
|
||||
def types
|
||||
{TYPE_LADDER => "Ladder", TYPE_LEAGUE => "League", TYPE_BRACKET => "Bracket"}
|
||||
end
|
||||
|
||||
def validate_status
|
||||
errors.add :status, I18n.t(:invalid_status) unless statuses.include? status
|
||||
end
|
||||
|
||||
def validate_contest_type
|
||||
errors.add :contest_type, I18n.t(:contests_invalidtype) unless types.include? contest_type
|
||||
end
|
||||
|
||||
def recalculate
|
||||
Match.update_all("diff = null, points1 = null, points2 = null", {:contest_id => self.id})
|
||||
Contester.update_all("score = 0, win = 0, loss = 0, draw = 0, extra = 0", {:contest_id => self.id})
|
||||
matches.finished.chrono.each do |match|
|
||||
match.recalculate
|
||||
match.save
|
||||
end
|
||||
end
|
||||
|
||||
def elo_score score1, score2, diff, level = self.modulus_base, weight = self.weight, moduluses = [self.modulus_even, self.modulus_3to1, self.modulus_4to0]
|
||||
points = (score2-score1).abs
|
||||
modulus = moduluses[0].to_f if points <= 1
|
||||
modulus = moduluses[1].to_f if points == 2
|
||||
modulus = moduluses[2].to_f if points >= 3
|
||||
if score1 == score2
|
||||
result = 0.5
|
||||
elsif score1 > score2
|
||||
result = 1.0
|
||||
elsif score2 > score1
|
||||
result = 0.0
|
||||
end
|
||||
prob = 1.0/(10**(diff.to_f/weight.to_f)+1.0)
|
||||
total = (level.to_f*modulus*(result-prob)).round
|
||||
return total
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
100
app/models/contester.rb
Normal file
100
app/models/contester.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
class Contester < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
TREND_FLAT = 0
|
||||
TREND_UP = 1
|
||||
TREND_DOWN = 2
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :trend
|
||||
attr_accessor :user
|
||||
|
||||
scope :active, :include => :team, :conditions => {"contesters.active" => true}
|
||||
scope :ordered, :select => "contesters.*, (score + extra) AS total_score", :order => "total_score DESC, score DESC, win DESC, loss ASC"
|
||||
scope :chronological, :order => "created_at DESC"
|
||||
scope :of_contest, lambda { |contest| {:conditions => {"contesters.contest_id" => contest.id}} }
|
||||
|
||||
has_many :challenges_sent, :class_name => "Challenge", :foreign_key => "contester1_id"
|
||||
has_many :challenges_received, :class_name => "Challenge", :foreign_key => "contester2_id"
|
||||
has_many :matches, :through => :contest, :conditions => "(contester1_id = contesters.id OR contester2_id = contesters.id)"
|
||||
belongs_to :team
|
||||
belongs_to :contest
|
||||
|
||||
validates_presence_of :team, :contest
|
||||
validates_inclusion_of [:score, :win, :loss, :draw, :extra], :in => 0..9999, :allow_nil => true
|
||||
validates_uniqueness_of :team_id, :scope => :contest_id, :message => "You can't join same contest twice."
|
||||
#validate_on_create:validate_member_participation
|
||||
#validate_on_create:validate_contest
|
||||
#validate_on_create:validate_playernumber
|
||||
|
||||
before_create :init_variables
|
||||
|
||||
|
||||
def to_s
|
||||
team.to_s
|
||||
end
|
||||
|
||||
def total
|
||||
score + extra.to_i
|
||||
end
|
||||
|
||||
def statuses
|
||||
{false => "Inactive", true => "Active"}
|
||||
end
|
||||
|
||||
def lineup
|
||||
contest.status == Contest::STATUS_CLOSED ? team.teamers.distinct : team.teamers.active
|
||||
end
|
||||
|
||||
def get_matches
|
||||
contest.matches.all :conditions => ["contester1_id = ? OR contester2_id = ?", id, id]
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.active = true
|
||||
self.trend = Contester::TREND_FLAT
|
||||
self.extra = 0
|
||||
end
|
||||
|
||||
def validate_member_participation
|
||||
# TODO joku erhe
|
||||
# for member in team.teamers.present do
|
||||
# for team in member.user.active_teams do
|
||||
# if team.contesters.active.exists?(:contest_id => contest_id)
|
||||
# errors.add_to_base "Member #{member.user} is already participating with team #{team.name}"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
||||
def validate_contest
|
||||
if contest.end.past? or contest.status == Contest::STATUS_CLOSED
|
||||
errors.add :contest, I18n.t(:contests_closed)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_playernumber
|
||||
if team.teamers.active.distinct.count < 6
|
||||
errors.add :team, I18n.t(:contests_join_need6)
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
update_attribute :active, false
|
||||
end
|
||||
|
||||
def can_create? cuser, params = {}
|
||||
return false unless cuser
|
||||
return false if cuser.banned?(Ban::TYPE_LEAGUE)
|
||||
return true if cuser.admin?
|
||||
return true if team.is_leader? cuser and Verification.contain params, [:team_id, :contest_id]
|
||||
return false
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and team.is_leader? cuser or cuser.admin?
|
||||
end
|
||||
end
|
137
app/models/data_file.rb
Normal file
137
app/models/data_file.rb
Normal file
|
@ -0,0 +1,137 @@
|
|||
require File.join(Rails.root, 'vendor', 'plugins', 'acts_as_rateable', 'init.rb')
|
||||
require 'digest/md5'
|
||||
|
||||
class DataFile < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
MEGABYTE = 1048576
|
||||
|
||||
attr_accessor :related_id
|
||||
attr_protected :id, :updated_at, :created_at, :path, :size, :md5
|
||||
|
||||
scope :recent, :order => "created_at DESC", :limit => 8
|
||||
scope :demos, :order => "created_at DESC", :conditions => ["directory_id IN (SELECT id FROM directories WHERE parent_id = ?)", Directory::DEMOS]
|
||||
scope :ordered, :order => "created_at DESC"
|
||||
scope :movies, :order => "created_at DESC", :conditions => {:directory_id => Directory::MOVIES}
|
||||
scope :not, lambda { |file| {:conditions => ["id != ?", file.id]} }
|
||||
scope :unrelated, :conditions => "related_id is null"
|
||||
|
||||
has_many :related_files, :class_name => "DataFile", :foreign_key => :related_id
|
||||
has_many :comments, :as => :commentable
|
||||
has_one :movie, :foreign_key => :file_id, :dependent => :destroy
|
||||
has_one :preview, :class_name => "Movie", :foreign_key => :preview_id, :dependent => :nullify
|
||||
has_one :match, :foreign_key => :demo_id
|
||||
belongs_to :directory
|
||||
belongs_to :related, :class_name => "DataFile"
|
||||
belongs_to :article
|
||||
|
||||
validates_length_of [:description, :path], :maximum => 255
|
||||
|
||||
before_save :process_file
|
||||
after_create :create_movie, :if => Proc.new {|file| file.directory_id == Directory::MOVIES and !file.location.include?("_preview.mp4") }
|
||||
after_save :update_relations, :if => Proc.new { |file| file.related_id_changed? and related_files.count > 0 }
|
||||
|
||||
acts_as_rateable
|
||||
mount_uploader :name, FileUploader
|
||||
|
||||
def to_s
|
||||
(description.nil? or description.empty?) ? File.basename(name.to_s) : description
|
||||
end
|
||||
|
||||
def md5_s
|
||||
md5.upcase
|
||||
end
|
||||
|
||||
def extra_url
|
||||
url.to_s.gsub(/^\/files/, "http://extra.ensl.org/static")
|
||||
end
|
||||
|
||||
def size_s
|
||||
(size.to_f/MEGABYTE).round(2).to_s + " MB"
|
||||
end
|
||||
|
||||
def location
|
||||
name.current_path
|
||||
end
|
||||
|
||||
def url
|
||||
name.url
|
||||
end
|
||||
|
||||
def process_file
|
||||
self.md5 = "e948c22100d29623a1df48e1760494df"
|
||||
if article
|
||||
self.directory_id = Directory::ARTICLES
|
||||
end
|
||||
|
||||
if File.exists?(location) and (size != File.size(location) or created_at != File.mtime(location))
|
||||
self.md5 = Digest::MD5.hexdigest(File.read(location))
|
||||
self.size = File.size(location)
|
||||
self.created_at = File.mtime(location)
|
||||
end
|
||||
|
||||
if path.nil? or directory_id_changed?
|
||||
self.path = File.join(directory.path, File.basename(name.to_s))
|
||||
end
|
||||
|
||||
if !new_record? and directory_id_changed? and File.exists?(name.current_path)
|
||||
FileUtils.mv(location, path)
|
||||
end
|
||||
|
||||
if description.nil? or description.empty?
|
||||
self.description = File.basename(location).gsub(/[_\-]/, " ").gsub(/\.\w{1,5}$/, "")
|
||||
self.description = description.split(/\s+/).each{ |word| word.capitalize! }.join(' ')
|
||||
end
|
||||
|
||||
if match
|
||||
self.description = match.contester1.to_s + " vs " + match.contester2.to_s
|
||||
end
|
||||
|
||||
if location.include? "_preview.mp4" and !related
|
||||
stripped = location.gsub(/_preview\.mp4/, "")
|
||||
DataFile.all(:conditions => ["path LIKE ?", stripped + "%"]).each do |r|
|
||||
if r.location.match(/#{stripped}\.\w{1,5}$/)
|
||||
self.related = r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if movie and (new_record? or md5_changed?)
|
||||
movie.get_length
|
||||
end
|
||||
end
|
||||
|
||||
def create_movie
|
||||
movie = Movie.new
|
||||
movie.file = self
|
||||
movie.make_snapshot 5
|
||||
movie.save
|
||||
end
|
||||
|
||||
def update_relations
|
||||
related_files.each do |rf|
|
||||
rf.related = self.related
|
||||
rf.save
|
||||
end
|
||||
end
|
||||
|
||||
def rateable? user
|
||||
user and !rated_by?(user)
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
return false unless cuser
|
||||
return false if cuser.banned?(Ban::TYPE_MUTE)
|
||||
(cuser.admin? or \
|
||||
(article and article.can_create? cuser) or \
|
||||
(directory_id == Directory::MOVIES and cuser.has_access? Group::MOVIES))
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin? or (article and article.can_create? cuser)
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin? or (article and article.can_create? cuser)
|
||||
end
|
||||
end
|
102
app/models/directory.rb
Normal file
102
app/models/directory.rb
Normal file
|
@ -0,0 +1,102 @@
|
|||
class Directory < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
ROOT = 1
|
||||
DEMOS = 5
|
||||
DEMOS_DEFAULT = 19
|
||||
DEMOS_GATHERS = 92
|
||||
MOVIES = 30
|
||||
ARTICLES = 39
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :path
|
||||
|
||||
belongs_to :parent, :class_name => "Directory"
|
||||
has_many :subdirs, :class_name => "Directory", :foreign_key => :parent_id
|
||||
has_many :files, :class_name => "DataFile", :order => "name"
|
||||
|
||||
scope :ordered, :order => "name ASC"
|
||||
scope :filtered, :conditions => {:hidden => false}
|
||||
scope :of_parent, lambda { |parent| {:conditions => {:parent_id => parent.id}} }
|
||||
|
||||
validates_length_of [:name, :path], :in => 1..255
|
||||
validates_format_of :name, :with => /\A[A-Za-z0-9]{1,20}\z/, :on => :create
|
||||
validates_length_of :name, :in => 1..25
|
||||
validates_inclusion_of :hidden, :in => [true, false]
|
||||
|
||||
before_validation :init_variables
|
||||
after_create :make_path
|
||||
after_save :update_timestamp
|
||||
before_destroy :remove_files
|
||||
after_destroy :remove_path
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.path = File.join(parent.path, name.downcase)
|
||||
self.hidden = false if hidden.nil?
|
||||
end
|
||||
|
||||
def make_path
|
||||
Dir.mkdir(path) unless File.exists?(path)
|
||||
end
|
||||
|
||||
def update_timestamp
|
||||
self.created_at = File.mtime(path) if File.exists?(path)
|
||||
end
|
||||
|
||||
def remove_files
|
||||
files.each do |subdir|
|
||||
subdir.destroy
|
||||
end
|
||||
subdirs.each do |subdir|
|
||||
subdir.destroy
|
||||
end
|
||||
end
|
||||
|
||||
def remove_path
|
||||
Dir.unlink(path) if File.exists?(path)
|
||||
end
|
||||
|
||||
def process_dir
|
||||
ActiveRecord::Base.transaction do
|
||||
Dir.glob("#{path}/*").each do |file|
|
||||
if File.directory?(file)
|
||||
if dir = Directory.find_by_path(file)
|
||||
dir.save!
|
||||
else
|
||||
dir = Directory.new
|
||||
dir.name = File.basename(file)
|
||||
dir.path = file
|
||||
dir.parent = self
|
||||
dir.save!
|
||||
end
|
||||
dir.process_dir
|
||||
else
|
||||
if dbfile = DataFile.find_by_path(file)
|
||||
dbfile.directory = self
|
||||
dbfile.save!
|
||||
elsif (File.mtime(file) + 100).past?
|
||||
dbfile = DataFile.new
|
||||
dbfile.path = file
|
||||
dbfile.directory = self
|
||||
dbfile.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser, params = {}
|
||||
cuser and cuser.admin? and Verification.contain params, [:description, :hidden]
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
64
app/models/forum.rb
Normal file
64
app/models/forum.rb
Normal file
|
@ -0,0 +1,64 @@
|
|||
class Forum < ActiveRecord::Base
|
||||
include Extra
|
||||
BANS = 8
|
||||
TRASH = 12
|
||||
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
|
||||
scope :available_to,
|
||||
lambda { |user, level| {
|
||||
:select => "forums.*, groupers.user_id AS access, COUNT(f2.id) AS acl, g2.user_id",
|
||||
:joins => "LEFT JOIN forumers ON forumers.forum_id = forums.id AND forumers.access = #{level}
|
||||
LEFT JOIN forumers AS f2 ON forumers.forum_id = forums.id AND f2.access = #{level}
|
||||
LEFT JOIN groups ON forumers.group_id = groups.id
|
||||
LEFT JOIN groupers ON groupers.group_id = groups.id AND groupers.user_id = #{user.id}
|
||||
LEFT JOIN groupers g2 ON g2.group_id = #{Group::ADMINS} AND g2.user_id = #{user.id}",
|
||||
:group => "forums.id",
|
||||
:having => ["access IS NOT NULL OR acl = 0 OR g2.user_id IS NOT NULL", level]} }
|
||||
scope :public,
|
||||
:select => "forums.*",
|
||||
:joins => "LEFT JOIN forumers ON forumers.forum_id = forums.id AND forumers.access = #{Forumer::ACCESS_READ}",
|
||||
:conditions => "forumers.id IS NULL"
|
||||
scope :of_forum,
|
||||
lambda { |forum| {:conditions => {"forums.id" => forum.id}} }
|
||||
scope :ordered, :order => "position"
|
||||
|
||||
has_many :topics
|
||||
has_many :posts, :through => :topics
|
||||
has_many :forumers
|
||||
has_many :groups, :through => :forumers
|
||||
has_one :forumer
|
||||
belongs_to :category
|
||||
|
||||
after_create :update_position
|
||||
|
||||
acts_as_readable
|
||||
|
||||
def to_s
|
||||
self.title
|
||||
end
|
||||
|
||||
def update_position
|
||||
update_attribute :position, self.id
|
||||
end
|
||||
|
||||
def can_show? cuser
|
||||
if cuser
|
||||
Forum.available_to(cuser, Forumer::ACCESS_READ).of_forum(self).first
|
||||
else
|
||||
Forum.public.of_forum(self).first
|
||||
end
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
27
app/models/forumer.rb
Normal file
27
app/models/forumer.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
class Forumer < ActiveRecord::Base
|
||||
ACCESS_READ = 0
|
||||
ACCESS_REPLY = 1
|
||||
ACCESS_TOPIC = 2
|
||||
|
||||
include Extra
|
||||
|
||||
scope :access,
|
||||
lambda { |level| {:conditions => ["access >= ?", level]} }
|
||||
|
||||
validates_uniqueness_of :group_id, :scope => [:forum_id, :access]
|
||||
validates_presence_of [:group_id, :forum_id]
|
||||
validates_inclusion_of :access, :in => 0..2
|
||||
|
||||
belongs_to :forum
|
||||
belongs_to :group
|
||||
|
||||
before_create :init_variables
|
||||
|
||||
def init_variables
|
||||
self.access ||= ACCESS_READ
|
||||
end
|
||||
|
||||
def accesses
|
||||
{ACCESS_READ => "Read", ACCESS_REPLY => "Reply", ACCESS_TOPIC => "Post a Topic"}
|
||||
end
|
||||
end
|
176
app/models/gather.rb
Normal file
176
app/models/gather.rb
Normal file
|
@ -0,0 +1,176 @@
|
|||
class Gather < ActiveRecord::Base
|
||||
STATE_RUNNING = 0
|
||||
STATE_VOTING = 3
|
||||
STATE_PICKING = 1
|
||||
STATE_FINISHED = 2
|
||||
NOTIFY = 6
|
||||
FULL = 12
|
||||
SERVERS = [3, 5, 23, 21, 22]
|
||||
|
||||
attr_accessor :admin
|
||||
|
||||
scope :ordered, :order => "id DESC"
|
||||
scope :basic, :include => [:captain1, :captain2, :map1, :map2, :server]
|
||||
scope :active,
|
||||
:conditions => ["gathers.status IN (?, ?, ?) AND gathers.updated_at > ?",
|
||||
STATE_VOTING, STATE_PICKING, STATE_RUNNING, 12.hours.ago.utc]
|
||||
|
||||
belongs_to :server
|
||||
belongs_to :captain1, :class_name => "Gatherer"
|
||||
belongs_to :captain2, :class_name => "Gatherer"
|
||||
belongs_to :map1, :class_name => "GatherMap"
|
||||
belongs_to :map2, :class_name => "GatherMap"
|
||||
belongs_to :category
|
||||
|
||||
has_many :gatherers
|
||||
has_many :users, :through => :gatherers
|
||||
has_many :gatherer_votes, :through => :gatherers, :source => :real_votes
|
||||
has_many :map_votes, :through => :gather_maps, :source => :real_votes
|
||||
has_many :gather_maps, :class_name => "GatherMap"
|
||||
has_many :maps, :through => :gather_maps
|
||||
has_many :server_votes, :through => :gather_servers, :source => :real_votes
|
||||
has_many :gather_servers, :class_name => "GatherServer"
|
||||
has_many :servers, :through => :gather_servers
|
||||
has_many :shoutmsgs, :as => "shoutable"
|
||||
has_many :real_votes, :class_name => "Vote", :as => :votable, :dependent => :destroy
|
||||
|
||||
before_create :init_variables
|
||||
after_create :add_maps_and_server
|
||||
before_save :check_status
|
||||
before_save :check_captains
|
||||
|
||||
def to_s
|
||||
"Gather_#{self.id}"
|
||||
end
|
||||
|
||||
def self.find_game(name)
|
||||
Category.where(name: name, domain: Category::DOMAIN_GAMES).first
|
||||
end
|
||||
|
||||
def self.player_count_for_game(name)
|
||||
game = self.find_game(name)
|
||||
|
||||
if game && players = game.gathers.ordered.first.gatherers
|
||||
players.size
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def demo_name
|
||||
Verification.uncrap("gather-#{self.id}")
|
||||
end
|
||||
|
||||
def states
|
||||
{STATE_RUNNING => "Running", STATE_PICKING => "Picking", STATE_FINISHED => "Finished"}
|
||||
end
|
||||
|
||||
def votes_needed?
|
||||
5
|
||||
end
|
||||
|
||||
def first
|
||||
Gather.where(:category_id => category_id).order("id ASC").first
|
||||
end
|
||||
|
||||
def previous_gather
|
||||
Gather.first(:conditions => ["id < ? AND category_id = ?", self.id, category_id], :order => "id DESC")
|
||||
end
|
||||
|
||||
def next_gather
|
||||
Gather.first(:conditions => ["id > ? AND category_id = ?", self.id, category_id], :order => "id ASC")
|
||||
end
|
||||
|
||||
def last
|
||||
Category.find(category_id).gathers.ordered.first
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.status = STATE_RUNNING
|
||||
end
|
||||
|
||||
def add_maps_and_server
|
||||
category.maps.basic.classic.each do |m|
|
||||
maps << m
|
||||
end
|
||||
|
||||
(category_id == 44 ? category.servers.hlds.active.ordered : category.servers.active.ordered).each do |s|
|
||||
servers << s
|
||||
end
|
||||
end
|
||||
|
||||
def check_status
|
||||
if status_changed? and status == STATE_PICKING and !self.captain1
|
||||
g = Gather.new
|
||||
g.category = self.category
|
||||
g.save
|
||||
self.captain1 = self.gatherers.most_voted[1]
|
||||
self.captain2 = self.gatherers.most_voted[0]
|
||||
if self.gather_maps.count > 1
|
||||
self.map1 = self.gather_maps.ordered[0]
|
||||
self.map2 = self.gather_maps.ordered[1]
|
||||
elsif self.gather_maps.count > 0
|
||||
self.map1 = self.gather_maps.ordered[0]
|
||||
end
|
||||
if self.gather_servers.count > 0
|
||||
self.server = self.gather_servers.ordered[0].server
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_captains
|
||||
if captain1_id_changed? or captain2_id_changed? or admin
|
||||
self.turn = 1
|
||||
self.status = STATE_PICKING
|
||||
gatherers.each do |gatherer|
|
||||
if gatherer.id == captain1_id
|
||||
gatherer.update_attributes(:team => 1, :skip_callbacks => true)
|
||||
elsif gatherer.id == captain2_id
|
||||
gatherer.update_attributes(:team => 2, :skip_callbacks => true)
|
||||
else
|
||||
gatherer.update_attributes(:team => nil, :skip_callbacks => true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def refresh cuser
|
||||
if status == STATE_RUNNING
|
||||
gatherers.idle.destroy_all
|
||||
elsif status == STATE_VOTING and updated_at < 60.seconds.ago and updated_at > 5.days.ago
|
||||
if status == STATE_VOTING and updated_at < 60.seconds.ago
|
||||
self.status = STATE_PICKING
|
||||
save!
|
||||
end
|
||||
elsif status == STATE_PICKING
|
||||
if turn == 1 and gatherers.team(1).count == 2 and gatherers.team(2).count == 1
|
||||
update_attribute :turn, 2
|
||||
elsif turn == 2 and gatherers.team(2).count == 3 and gatherers.team(1).count == 2
|
||||
update_attribute :turn, 1
|
||||
elsif turn == 1 and gatherers.team(1).count == 4 and gatherers.team(2).count == 3
|
||||
update_attribute :turn, 2
|
||||
elsif turn == 2 and gatherers.team(2).count == 5 and gatherers.team(1).count == 4
|
||||
update_attribute :turn, 1
|
||||
elsif turn == 1 and gatherers.team(1).count == 6 and gatherers.team(2).count == 5
|
||||
gatherers.lobby.first.update_attributes(:team => 2, :skip_callbacks => true)
|
||||
update_attribute :turn, 2
|
||||
elsif gatherers.team(1).count == 6 and gatherers.team(2).count == 6
|
||||
update_attribute :status, STATE_FINISHED
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def self.last(name = "NS2")
|
||||
if game = self.find_game(name)
|
||||
game.gathers.ordered.first
|
||||
end
|
||||
end
|
||||
end
|
17
app/models/gather_map.rb
Normal file
17
app/models/gather_map.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
class GatherMap < ActiveRecord::Base
|
||||
scope :ordered, :order => "votes DESC, id DESC"
|
||||
|
||||
belongs_to :gather
|
||||
belongs_to :map
|
||||
has_many :real_votes, :class_name => "Vote", :as => :votable
|
||||
|
||||
before_create :init_variables
|
||||
|
||||
def to_s
|
||||
self.map.to_s
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.votes = 0
|
||||
end
|
||||
end
|
15
app/models/gather_server.rb
Normal file
15
app/models/gather_server.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
class GatherServer < ActiveRecord::Base
|
||||
scope :ordered, :order => "votes DESC"
|
||||
|
||||
belongs_to :gather
|
||||
belongs_to :server
|
||||
has_many :real_votes, :class_name => "Vote", :as => :votable
|
||||
|
||||
def to_s
|
||||
self.server.to_s
|
||||
end
|
||||
|
||||
def before_create
|
||||
self.votes = 0
|
||||
end
|
||||
end
|
153
app/models/gatherer.rb
Normal file
153
app/models/gatherer.rb
Normal file
|
@ -0,0 +1,153 @@
|
|||
class Gatherer < ActiveRecord::Base
|
||||
IDLE_TIME = 600
|
||||
EJECT_VOTES = 4
|
||||
|
||||
include Extra
|
||||
|
||||
attr_protected :id
|
||||
attr_accessor :confirm, :username
|
||||
cattr_accessor :skip_callbacks
|
||||
|
||||
scope :team,
|
||||
lambda { |team| {:conditions => {:team => team}} }
|
||||
scope :of_user,
|
||||
lambda { |user| {:conditions => {:user_id => user.id}} }
|
||||
scope :lobby, :conditions => "team IS NULL"
|
||||
scope :best,
|
||||
lambda { |gather| {
|
||||
:select => "u.id, u.username, (COUNT(*) / (SELECT COUNT(*) FROM gatherers g3 WHERE g3.user_id = u.id)) AS skill, g4.id",
|
||||
:from => "gathers g1",
|
||||
:joins => "LEFT JOIN gatherers g2 ON g1.captain1_id = g2.id OR g1.captain2_id = g2.id
|
||||
LEFT JOIN users u ON g2.user_id = u.id
|
||||
LEFT JOIN gatherers g4 ON u.id = g4.user_id AND g4.gather_id = #{gather.id}",
|
||||
:group => "u.id",
|
||||
:having => "g4.id IS NOT NULL",
|
||||
:order => "skill DESC",
|
||||
:limit => 15 } }
|
||||
scope :with_kpd,
|
||||
:select => "gatherers.*, SUM(kills)/SUM(deaths) as kpd, COUNT(rounders.id) as rounds",
|
||||
:joins => "LEFT JOIN rounders ON rounders.user_id = gatherers.user_id",
|
||||
:group => "rounders.user_id",
|
||||
:order => "kpd DESC"
|
||||
scope :lobby_team,
|
||||
lambda { |team| {
|
||||
:conditions => ["gatherers.team IS NULL OR gatherers.team = ?", team],
|
||||
:order => "gatherers.team"} }
|
||||
scope :most_voted, :order => "votes DESC, created_at DESC"
|
||||
scope :not_user,
|
||||
lambda { |user| {:conditions => ["user_id != ?", user.id]} }
|
||||
scope :eject_order, :order => "votes ASC"
|
||||
scope :ordered,
|
||||
:joins => "LEFT JOIN gathers ON captain1_id = gatherers.id OR captain2_id = gatherers.id",
|
||||
:order => "captain1_id, captain2_id, gatherers.id"
|
||||
scope :idle,
|
||||
:joins => "LEFT JOIN users ON users.id = gatherers.user_id",
|
||||
:conditions => ["lastvisit < ?", 30.minutes.ago.utc]
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :gather
|
||||
has_many :real_votes, :class_name => "Vote", :as => :votable, :dependent => :destroy
|
||||
|
||||
validates_uniqueness_of :user_id, :scope => :gather_id
|
||||
validates_inclusion_of :team, :in => 1..2, :allow_nil => true
|
||||
validates :confirm, :acceptance => true, :unless => Proc.new {|gatherer| gatherer.user.gatherers.count >= 5}
|
||||
validate :validate_username
|
||||
|
||||
after_create :start_gather, :if => Proc.new {|gatherer| gatherer.gather.gatherers.count == Gather::FULL}
|
||||
after_create :notify_gatherers, :if => Proc.new {|gatherer| gatherer.gather.gatherers.count == Gather::NOTIFY}
|
||||
after_update :change_turn, :unless => Proc.new {|gatherer| gatherer.skip_callbacks == true}
|
||||
after_destroy :cleanup_votes
|
||||
|
||||
def to_s
|
||||
user.to_s
|
||||
end
|
||||
|
||||
def validate_username
|
||||
if username
|
||||
if u = User.first(:conditions => {:username => username})
|
||||
self.user = u
|
||||
else
|
||||
errors.add(:username, t(:gatherer_wrong_username))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def start_gather
|
||||
gather.update_attribute :status, Gather::STATE_VOTING
|
||||
end
|
||||
|
||||
def notify_gatherers
|
||||
Profile.all(:include => :user, :conditions => "notify_gather = 1").each do |p|
|
||||
Notifications.gather p.user, gather if p.user
|
||||
end
|
||||
end
|
||||
|
||||
def change_turn
|
||||
if team_changed? and team != nil
|
||||
new_turn = (team == 1 ? 2 : 1)
|
||||
if team == 2 and [2, 4].include?(gather.gatherers.team(2).count.to_i)
|
||||
new_turn = 2
|
||||
elsif team == 1 and [3, 5].include?(gather.gatherers.team(1).count.to_i)
|
||||
new_turn = 1
|
||||
end
|
||||
gather.update_attribute :turn, new_turn
|
||||
if gather.gatherers.lobby.count == 1
|
||||
gather.gatherers.lobby.first.update_attribute :team, (self.team == 1 ? 2 : 1)
|
||||
end
|
||||
if gather.gatherers.lobby.count == 0
|
||||
gather.update_attribute :status, Gather::STATE_FINISHED
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cleanup_votes
|
||||
gather.map_votes.all(:conditions => {:user_id => user_id}).each { |g| g.destroy }
|
||||
gather.server_votes.all(:conditions => {:user_id => user_id}).each { |g| g.destroy }
|
||||
gather.gatherer_votes.all(:conditions => {:user_id => user_id}).each { |g| g.destroy }
|
||||
end
|
||||
|
||||
def votes_needed?
|
||||
return 5
|
||||
end
|
||||
|
||||
def captain?
|
||||
gather.captain1 == self or gather.captain2 == self
|
||||
end
|
||||
|
||||
def turn?
|
||||
(gather.captain1 == self and gather.turn == 1) or (gather.captain2 == self and gather.turn == 2)
|
||||
end
|
||||
|
||||
def can_create? cuser, params = {}
|
||||
# and check_params(params, [:user_id, :gather_id])
|
||||
cuser \
|
||||
and user == cuser \
|
||||
and !cuser.banned?(Ban::TYPE_GATHER) \
|
||||
and gather.status == Gather::STATE_RUNNING \
|
||||
and gather.gatherers.count < Gather::FULL \
|
||||
and !gather.gatherers.of_user(cuser).first
|
||||
end
|
||||
|
||||
def can_update? cuser, params = {}
|
||||
return false unless cuser
|
||||
if params.keys.include? "username"
|
||||
if cuser.admin?
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false unless team.nil? \
|
||||
and ((gather.captain1.user == cuser and gather.turn == 1) or (gather.captain2.user == cuser and gather.turn == 2))
|
||||
return false if gather.turn == 1 and gather.gatherers.team(1).count == 2 and gather.gatherers.team(2).count < 3
|
||||
return false if gather.turn == 2 and gather.gatherers.team(1).count < 4 and gather.gatherers.team(2).count == 3
|
||||
return false if gather.turn == 1 and gather.gatherers.team(1).count == 4 and gather.gatherers.team(2).count < 5
|
||||
return false if gather.turn == 2 and gather.gatherers.team(1).count < 6 and gather.gatherers.team(2).count == 5
|
||||
return false if gather.turn == 1 and gather.gatherers.team(1).count == 6
|
||||
true
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and ((user == cuser or cuser.admin?) and gather.status == Gather::STATE_RUNNING)
|
||||
end
|
||||
end
|
77
app/models/group.rb
Normal file
77
app/models/group.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
class Group < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
ADMINS = 1
|
||||
REFEREES = 2
|
||||
MOVIES = 3
|
||||
DONORS = 4
|
||||
MOVIEMAKERS = 5
|
||||
SHOUTCASTERS = 6
|
||||
CHAMPIONS = 7
|
||||
PREDICTORS = 8
|
||||
STAFF = 10
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :founder_id
|
||||
validates_length_of :name, :maximum => 20
|
||||
|
||||
has_and_belongs_to_many :users
|
||||
has_many :groupers
|
||||
has_many :users, :through => :groupers
|
||||
belongs_to :founder, :class_name => "User"
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def self.staff
|
||||
staff = []
|
||||
(find(ADMINS).groupers + find(PREDICTORS).groupers + find(SHOUTCASTERS).groupers + find(STAFF).groupers + find(REFEREES).groupers).each do |g|
|
||||
staff << g unless staff.include? g
|
||||
end
|
||||
staff
|
||||
end
|
||||
|
||||
def self.admins
|
||||
admins = []
|
||||
(find(ADMINS).groupers).each do |g|
|
||||
admins << g unless admins.include? g
|
||||
end
|
||||
admins
|
||||
end
|
||||
|
||||
def self.referees
|
||||
referees = []
|
||||
(find(REFEREES).groupers).each do |g|
|
||||
referees << g unless referees.include? g
|
||||
end
|
||||
referees
|
||||
end
|
||||
|
||||
def self.extras
|
||||
extras = []
|
||||
(find(PREDICTORS).groupers + find(STAFF).groupers).each do |g|
|
||||
extras << g unless extras.include? g
|
||||
end
|
||||
extras
|
||||
end
|
||||
|
||||
def self.shoutcasters
|
||||
shoutcasters = []
|
||||
(find(SHOUTCASTERS).groupers).each do |g|
|
||||
shoutcasters << g unless shoutcasters.include? g
|
||||
end
|
||||
shoutcasters
|
||||
end
|
||||
end
|
33
app/models/grouper.rb
Normal file
33
app/models/grouper.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
class Grouper < ActiveRecord::Base
|
||||
attr_protected :id, :created_at, :updated_at
|
||||
attr_accessor :username
|
||||
|
||||
belongs_to :group
|
||||
belongs_to :user
|
||||
|
||||
validates_associated :group, :user
|
||||
validates :group, :user, :presence => true
|
||||
validates :task, :length => {:maximum => 25}
|
||||
|
||||
before_validation :fetch_user, :if => Proc.new {|grouper| grouper.username and !grouper.username.empty?}
|
||||
|
||||
def to_s
|
||||
user.to_s
|
||||
end
|
||||
|
||||
def fetch_user
|
||||
self.user = User.find_by_username(username)
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
89
app/models/issue.rb
Normal file
89
app/models/issue.rb
Normal file
|
@ -0,0 +1,89 @@
|
|||
require File.join(Rails.root, 'vendor', 'plugins', 'acts-as-readable', 'init.rb')
|
||||
require 'rbbcode'
|
||||
|
||||
class Issue < ActiveRecord::Base
|
||||
STATUS_OPEN = 0
|
||||
STATUS_SOLVED = 1
|
||||
STATUS_REJECTED = 2
|
||||
|
||||
attr_accessor :assigned_name
|
||||
attr_protected :id, :created_at, :updated_at
|
||||
|
||||
has_many :comments, :as => :commentable
|
||||
belongs_to :category
|
||||
belongs_to :author, :class_name => "User"
|
||||
belongs_to :assigned, :class_name => "User"
|
||||
|
||||
scope :unread_by,
|
||||
lambda { |user| {
|
||||
:joins => "LEFT JOIN readings ON readable_type = 'Issue' AND readable_id = issues.id AND readings.user_id = #{user.id}",
|
||||
:conditions => "readings.user_id IS NULL"} }
|
||||
scope :with_status, lambda { |s| { :conditions => {:status => s}} }
|
||||
|
||||
validates_length_of :title, :in => 1..50
|
||||
validates_length_of :text, :in => 1..65000
|
||||
validate :validate_status
|
||||
|
||||
before_validation :init_variables, :if => Proc.new{|issue| issue.new_record?}
|
||||
before_save :parse_text
|
||||
after_save :remove_readings
|
||||
|
||||
acts_as_readable
|
||||
|
||||
def to_s
|
||||
title
|
||||
end
|
||||
|
||||
def status_s
|
||||
statuses[status]
|
||||
end
|
||||
|
||||
def statuses
|
||||
{STATUS_OPEN => "Open", STATUS_SOLVED => "Solved", STATUS_REJECTED => "Rejected"}
|
||||
end
|
||||
|
||||
def color
|
||||
if status == STATUS_SOLVED
|
||||
"green"
|
||||
elsif status == STATUS_OPEN
|
||||
"yellow"
|
||||
elsif status == STATUS_REJECTED
|
||||
"red"
|
||||
end
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.assigned = User.find_by_username(assigned_name) if assigned_name
|
||||
self.status = STATUS_OPEN unless status
|
||||
end
|
||||
|
||||
def validate_status
|
||||
errors.add :status, I18n.t(:invalid_status) unless statuses.include? status
|
||||
end
|
||||
|
||||
def parse_text
|
||||
self.text_parsed = RbbCode::Parser.new.parse(text)
|
||||
end
|
||||
|
||||
def remove_readings
|
||||
if status_changed? and status == STATUS_SOLVED
|
||||
Reading.delete_all ["readable_type = 'Issue' AND readable_id = ?", self.id]
|
||||
end
|
||||
end
|
||||
|
||||
def can_show? cuser
|
||||
cuser and !cuser.nil? and ((author == cuser) or cuser.admin?)
|
||||
end
|
||||
|
||||
def can_create? cuser, params = {}
|
||||
Verification.contain params, [:title, :category_id, :text]
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
12
app/models/lock.rb
Normal file
12
app/models/lock.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class Lock < ActiveRecord::Base
|
||||
include Extra
|
||||
belongs_to :lockable, :polymorphic => true
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
185
app/models/log.rb
Normal file
185
app/models/log.rb
Normal file
|
@ -0,0 +1,185 @@
|
|||
class Log < ActiveRecord::Base
|
||||
include Extra
|
||||
attr_accessor :text
|
||||
|
||||
DOMAIN_LOG = 1
|
||||
DOMAIN_RCON_COMMAND = 2
|
||||
DOMAIN_RCON_RESPONSE = 3
|
||||
DOMAIN_INFO = 4
|
||||
|
||||
TEAM_MARINES = 1
|
||||
TEAM_ALIENS = 2
|
||||
|
||||
RE_PLAYER = /".*?<\d*><STEAM_\d*:\d*:\d*><\w*>"/
|
||||
RE_PLAYER_ID = /".*?<\d*><STEAM_(\d*:\d*:\d*)><\w*>"/
|
||||
RE_PLAYER_ID_NAME_TEAM = /"(.*?)<\d*><STEAM_(\d*:\d*:\d*)><([a-z]*)1team>"/
|
||||
RE_PLAYER_NAME = /"(.*?)<\d*><STEAM_\d*:\d*:\d*><[a-z]*1team>"/
|
||||
RE_PLAYER_NAME_TEAM = /"(.*?)<\d*><STEAM_\d*:\d*:\d*><([a-z]*)1team>"/
|
||||
|
||||
scope :recent, :order => "id DESC", :limit => 5
|
||||
scope :ordered, :order => "created_at ASC, id ASC"
|
||||
scope :with_details,
|
||||
lambda { |details| {:conditions => ["details LIKE ?", details]} }
|
||||
scope :unhandled, :conditions => {:details => nil}
|
||||
scope :stats,
|
||||
:select => "id, details, COUNT(*) as num",
|
||||
:group => "details",
|
||||
:order => "details"
|
||||
|
||||
belongs_to :details, :class_name => "LogEvent"
|
||||
belongs_to :server
|
||||
belongs_to :round
|
||||
belongs_to :server
|
||||
belongs_to :log_file
|
||||
belongs_to :actor, :class_name => "Rounder"
|
||||
belongs_to :target, :class_name => "Rounder"
|
||||
|
||||
# def domains
|
||||
# return {DOMAIN_LOG => "HL Log", DOMAIN_RCON_COMMAND => "Rcon Command Log", DOMAIN_RCON_RESPONSE => "Rcon Response Log", DOMAIN_INFO => "Action Log"}
|
||||
# end
|
||||
|
||||
def since
|
||||
(created_at - round.start).to_i
|
||||
end
|
||||
|
||||
def time
|
||||
return sprintf("%02d:%02d", since/60, since%60)
|
||||
end
|
||||
|
||||
def frag
|
||||
text.match(/^#{RE_PLAYER_NAME_TEAM} killed #{RE_PLAYER_NAME_TEAM} with "([a-z0-9_]*)"$/)
|
||||
end
|
||||
|
||||
def role
|
||||
text.match(/^#{RE_PLAYER_NAME} changed role to "([a-z0-9_]*)"$/)
|
||||
end
|
||||
|
||||
def match_map vars
|
||||
if m = text.match(/^Started map "([A-Za-z0-9_]*)"/)
|
||||
vars[:map] = m[1]
|
||||
self.details = LogEvent.get "map"
|
||||
self.specifics1 = m[1]
|
||||
end
|
||||
end
|
||||
|
||||
def match_start vars
|
||||
if text.match(/^Game reset complete.$/)
|
||||
vars[:round] = Round.new
|
||||
vars[:round].server = server
|
||||
vars[:round].start = created_at
|
||||
vars[:round].map_name = vars[:map]
|
||||
vars[:round].map = Map.with_name(vars[:map]).first
|
||||
vars[:round].save
|
||||
vars[:lifeforms] = {}
|
||||
self.details = LogEvent.get "start"
|
||||
end
|
||||
end
|
||||
|
||||
def match_end vars
|
||||
if m = text.match(/^Team ([1-2]) has lost.$/)
|
||||
vars[:round].winner = (m[1].to_i == 1 ? 2 : 1)
|
||||
vars[:round].end = created_at
|
||||
[1, 2].each do |team|
|
||||
if s = vars[:round].rounders.team(team).stats.first
|
||||
vars[:round]["team#{team}_id"] = s["team_id"]
|
||||
end
|
||||
end
|
||||
vars[:round].save
|
||||
vars[:round] = nil
|
||||
self.details = LogEvent.get "end"
|
||||
end
|
||||
end
|
||||
|
||||
def match_join vars
|
||||
if m = text.match(/^#{RE_PLAYER_ID_NAME_TEAM} .*$/) and !(self.actor = vars[:round].rounders.match(m[2]).first)
|
||||
self.actor = Rounder.new
|
||||
self.actor.round = vars[:round]
|
||||
self.actor.name = m[1]
|
||||
self.actor.steamid = m[2]
|
||||
self.actor.user = User.first(:conditions => {:steamid => m[2]}) or User.historic(m[2])
|
||||
self.actor.team = (m[3] == "marine" ? TEAM_MARINES : TEAM_ALIENS)
|
||||
if self.actor.user and t = Teamer.historic(actor.user, vars[:round].start).first
|
||||
self.actor.ensl_team = t.team
|
||||
end
|
||||
self.actor.kills = 0
|
||||
self.actor.deaths = 0
|
||||
self.actor.save
|
||||
self.details = LogEvent.get "join"
|
||||
end
|
||||
end
|
||||
|
||||
def match_kill vars
|
||||
if m = text.match(/^#{RE_PLAYER} killed #{RE_PLAYER_ID} with "([a-z0-9_]*)"$/)
|
||||
if self.actor
|
||||
actor.increment :kills
|
||||
actor.save
|
||||
end
|
||||
if self.target = vars[:round].rounders.match(m[1]).first
|
||||
target.increment :deaths
|
||||
target.save
|
||||
end
|
||||
self.details = LogEvent.get "kill"
|
||||
self.specifics1 = m[3]
|
||||
save
|
||||
end
|
||||
end
|
||||
|
||||
def match_say vars
|
||||
if m = text.match(/^#{RE_PLAYER} (say(_team)?) ".*"$/)
|
||||
self.details = "say"
|
||||
self.specifics1 = m[1]
|
||||
end
|
||||
end
|
||||
|
||||
def match_built vars
|
||||
if m = text.match(/^#{RE_PLAYER} triggered "structure_built" \(type "([a-z0-9_]*)"\)$/)
|
||||
self.details = "built_" + m[1]
|
||||
end
|
||||
end
|
||||
|
||||
def match_destroyed vars
|
||||
if m = text.match(/^#{RE_PLAYER} triggered "structure_destroyed" \(type "([a-z0-9_]*)"\)$/)
|
||||
self.details = "destroyed_" + m[1]
|
||||
end
|
||||
end
|
||||
|
||||
def match_research_start vars
|
||||
if m = text.match(/^#{RE_PLAYER} triggered "research_start" \(type "([a-z0-9_]*)"\)$/)
|
||||
self.details = m[1]
|
||||
end
|
||||
end
|
||||
|
||||
def match_research_cancel vars
|
||||
if m = text.match(/^#{RE_PLAYER} triggered "research_cancel" \(type "([a-z0-9_]*)"\)$/)
|
||||
self.details = "research_cancel"
|
||||
end
|
||||
end
|
||||
|
||||
def match_role vars
|
||||
if m = text.match(/^#{RE_PLAYER_ID} changed role to "([a-z0-9_]*)"$/)
|
||||
if m[2] == "gestate"
|
||||
self.details = "gestate"
|
||||
elsif actor
|
||||
if m[2] == "commander" and !vars[:round].commander
|
||||
vars[:round].commander = actor
|
||||
vars[:round].save
|
||||
end
|
||||
if !actor.roles
|
||||
actor.update_attribute :roles, m[2]
|
||||
elsif !self.actor.roles.include?(m[2])
|
||||
actor.update_attribute :roles, actor.roles + ", " + m[2]
|
||||
end
|
||||
self.details = ((vars[:lifeforms].include? actor.id and vars[:lifeforms][actor.id] == m[2]) ? "upgrade" : m[2])
|
||||
vars[:lifeforms][actor.id] = m[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.add server, domain, text
|
||||
log = new
|
||||
log.server = server
|
||||
log.domain = domain
|
||||
log.text = text
|
||||
log.save
|
||||
end
|
||||
end
|
13
app/models/log_event.rb
Normal file
13
app/models/log_event.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
class LogEvent < ActiveRecord::Base
|
||||
def self.get search, team = nil
|
||||
if f = first({:conditions => {:name => search}})
|
||||
return f
|
||||
else
|
||||
f = LogEvent.new
|
||||
f.name = "get"
|
||||
f.team = team if team
|
||||
f.save
|
||||
return f
|
||||
end
|
||||
end
|
||||
end
|
83
app/models/log_file.rb
Normal file
83
app/models/log_file.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
# encoding: US-ASCII
|
||||
|
||||
require 'digest/md5'
|
||||
|
||||
class LogFile < ActiveRecord::Base
|
||||
NON_ASCII = /[\x80-\xff]/
|
||||
LOGS = File.join(Rails.root, "tmp", "logs")
|
||||
|
||||
attr_accessor :path
|
||||
belongs_to :server
|
||||
|
||||
has_many :logs
|
||||
has_many :rounds, :through => :logs
|
||||
|
||||
def after_create
|
||||
Pathname(path).each_line do |line|
|
||||
if m = line.gsub(NON_ASCII, "").match(/\d{2}:\d{2}:\d{2}: (.*)/)
|
||||
log = Log.new
|
||||
log.server = server
|
||||
log.domain = Log::DOMAIN_LOG
|
||||
log.log_file = self
|
||||
log.text = m[1].strip
|
||||
next if log.text.match(/^Server cvar/)
|
||||
next if log.text.match(/^\[ENSL\]/)
|
||||
next if log.text.match(/STEAM USERID validated/)
|
||||
next if log.text.match(/^\[META\]/)
|
||||
l.created_at = DateTime.parse(line.match(/\d{2}\/\d{2}\/\d{4} \- \d{2}:\d{2}:\d{2}:/)[0])
|
||||
vars = {}
|
||||
log.match_map vars or log.match_start vars
|
||||
if vars[:round] and !log.details
|
||||
log.match_end vars \
|
||||
or log.match_join vars \
|
||||
or log.match_kill vars \
|
||||
or log.match_say vars \
|
||||
or log.match_built vars \
|
||||
or log.match_destroyed vars \
|
||||
or log.match_research_start vars \
|
||||
or log.match_research_cancel vars \
|
||||
or log.match_role vars \
|
||||
end
|
||||
if log.details
|
||||
log.round = vars[:round] if vars[:round]
|
||||
log.save
|
||||
end
|
||||
end
|
||||
end
|
||||
rounds.each do |r|
|
||||
unless r.end
|
||||
r.destroy
|
||||
end
|
||||
end
|
||||
Log.delete_all(["details IS NULL AND log_file_id = ?", self.id])
|
||||
end
|
||||
|
||||
def format path
|
||||
self.name = File.basename(path)
|
||||
self.size = File.size(path)
|
||||
self.md5 = Digest::MD5.hexdigest(File.read(path))
|
||||
self.updated_at = File.mtime(path)
|
||||
self.path = path
|
||||
end
|
||||
|
||||
def deal
|
||||
# TODO
|
||||
end
|
||||
|
||||
def self.process
|
||||
Dir.glob("#{LOGS}/*").each do |entry|
|
||||
dir = File.basename(entry).to_i
|
||||
if File.directory?(entry) and dir > 0 and Server.find(dir)
|
||||
Dir.glob("#{entry}/*.log").each do |file|
|
||||
lf = LogFile.new
|
||||
lf.format file
|
||||
lf.server_id = dir
|
||||
|
||||
unless LogFile.first(:conditions => {:name => lf.name, :size => lf.size, :server_id => dir.to_i})
|
||||
lf.save
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
39
app/models/map.rb
Normal file
39
app/models/map.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
class Map < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :deleted
|
||||
|
||||
has_and_belongs_to_many :contests
|
||||
|
||||
scope :basic, :conditions => {:deleted => false}, :order => "name"
|
||||
scope :with_name, lambda { |name| {:conditions => {:name => name}} }
|
||||
scope :classic, :conditions => "name LIKE 'ns_%'"
|
||||
scope :of_category,
|
||||
lambda { |category| {
|
||||
:conditions => {:category_id => category.id} }}
|
||||
|
||||
validates_length_of :name, :maximum => 20
|
||||
validates_length_of :download, :maximum => 100
|
||||
|
||||
mount_uploader :picture, MapUploader
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def destroy
|
||||
update_attribute :deleted, true
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
288
app/models/match.rb
Normal file
288
app/models/match.rb
Normal file
|
@ -0,0 +1,288 @@
|
|||
class Match < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
MATCH_LENGTH = 7200
|
||||
|
||||
include Exceptions
|
||||
|
||||
attr_accessor :lineup, :method, :motm_name, :friendly
|
||||
attr_protected :id, :updated_at, :created_at, :diff, :points1, :points2
|
||||
|
||||
has_many :matchers, :dependent => :destroy
|
||||
has_many :users, :through => :matchers
|
||||
has_many :predictions, :dependent => :destroy
|
||||
has_many :comments, :as => :commentable, :order => "created_at", :dependent => :destroy
|
||||
belongs_to :challenge
|
||||
belongs_to :contest
|
||||
belongs_to :contester1, :class_name => "Contester", :include => 'team'
|
||||
belongs_to :contester2, :class_name => "Contester", :include => 'team'
|
||||
belongs_to :map1, :class_name => "Map"
|
||||
belongs_to :map2, :class_name => "Map"
|
||||
belongs_to :server
|
||||
belongs_to :referee, :class_name => "User"
|
||||
belongs_to :motm, :class_name => "User"
|
||||
belongs_to :demo, :class_name => "DataFile"
|
||||
belongs_to :week
|
||||
belongs_to :hltv, :class_name => "Server"
|
||||
belongs_to :stream, :class_name => "Movie"
|
||||
|
||||
scope :future, :conditions => ["match_time > UTC_TIMESTAMP()"]
|
||||
scope :future5, :conditions => ["match_time > UTC_TIMESTAMP()"], :limit => 5
|
||||
scope :finished, :conditions => ["score1 != 0 OR score2 != 0"]
|
||||
scope :realfinished, :conditions => ["score1 IS NOT NULL AND score2 IS NOT NULL"]
|
||||
scope :unfinished, :conditions => ["score1 IS NULL AND score2 IS NULL"]
|
||||
scope :unreffed, :conditions => ["referee_id IS NULL"]
|
||||
scope :ordered, :order => "match_time DESC"
|
||||
scope :chrono, :order => "match_time ASC"
|
||||
scope :recent, :limit => "8"
|
||||
scope :bigrecent, :limit => "50"
|
||||
scope :active, :conditions => ["contest_id IN (?)", Contest.active]
|
||||
scope :on_day,
|
||||
lambda { |day| {
|
||||
:conditions => ["match_time > ? and match_time < ?", day.beginning_of_day, day.end_of_day]} }
|
||||
scope :on_week,
|
||||
lambda { |time| {
|
||||
:conditions => ["match_time > ? and match_time < ?", time.beginning_of_week, time.end_of_week]} }
|
||||
scope :of_contester,
|
||||
lambda { |contester| {
|
||||
:conditions => ["contester1_id = ? OR contester2_id = ?", contester.id, contester.id]} }
|
||||
scope :of_user,
|
||||
lambda { |user| {
|
||||
:include => :matchers,
|
||||
:conditions => ["matchers.user_id = ?", user.id]} }
|
||||
scope :of_team,
|
||||
lambda { |team| {
|
||||
:include => {:contester1 => :team,
|
||||
:contester2 => :team}, :conditions => ["teams.id = ? OR teams_contesters.id = ?", team.id, team.id]} }
|
||||
scope :of_userteam,
|
||||
lambda { |user, team| {
|
||||
:include => {:matchers => {:contester => :team}},
|
||||
:conditions => ["teams.id = ? AND matchers.user_id = ?", team.id, user.id]} }
|
||||
scope :within_time,
|
||||
lambda { |from, to| {
|
||||
:conditions => ["match_time > ? AND match_time < ?", from.utc, to.utc]} }
|
||||
scope :around,
|
||||
lambda { |time| {
|
||||
:conditions => ["match_time > ? AND match_time < ?", time.ago(MATCH_LENGTH).utc, time.ago(-MATCH_LENGTH).utc]} }
|
||||
scope :after,
|
||||
lambda { |time| {
|
||||
:conditions => ["match_time > ? AND match_time < ?", time.utc, time.ago(-MATCH_LENGTH).utc]} }
|
||||
scope :map_stats,
|
||||
:select => "map1_id, COUNT(*) as num, maps.name",
|
||||
:joins => "LEFT JOIN maps ON maps.id = map1_id",
|
||||
:group => "map1_id",
|
||||
:having => "map1_id is not null",
|
||||
:order => "num DESC"
|
||||
scope :year_stats,
|
||||
:select => "id, DATE_FORMAT(match_time, '%Y') as year, COUNT(*) as num",
|
||||
:conditions => "match_time > '2000-01-01 01:01:01'",
|
||||
:group => "year",
|
||||
:order => "num DESC"
|
||||
scope :month_stats,
|
||||
:select => "id, DATE_FORMAT(match_time, '%m') as month_n,
|
||||
DATE_FORMAT(match_time, '%M') as month,
|
||||
COUNT(*) as num",
|
||||
:conditions => "match_time > '2000-01-01 01:01:01'",
|
||||
:group => "month",
|
||||
:order => "month_n"
|
||||
|
||||
validates_presence_of :contester1, :contester2, :contest
|
||||
validates_format_of [:score1, :score2], :with => /\A[0-9]\z/, :allow_nil => true
|
||||
validates_length_of :report, :maximum => 64000, :allow_blank => true
|
||||
|
||||
before_create :set_hltv
|
||||
after_create :send_notifications
|
||||
before_save :set_motm, :if => Proc.new {|match| match.motm_name and !match.motm_name.empty?}
|
||||
after_save :recalculate, :if => Proc.new {|match| match.score1_changed? or match.score2_changed?}
|
||||
after_save :set_predictions, :if => Proc.new {|match| match.score1_changed? or match.score2_changed?}
|
||||
|
||||
accepts_nested_attributes_for :matchers, :allow_destroy => true
|
||||
|
||||
def to_s
|
||||
contester1.to_s + " vs " + contester2.to_s
|
||||
end
|
||||
|
||||
def score_color
|
||||
return "black" if score1.nil? or score2.nil? or contester1.nil? or contester2.nil?
|
||||
return "yellow" if score1 == score2
|
||||
return "green" if contester1.team == friendly and score1 > score2
|
||||
return "green" if contester2.team == friendly and score2 > score1
|
||||
return "red" if contester1.team == friendly and score1 < score2
|
||||
"red" if contester2.team == friendly and score2 < score1
|
||||
end
|
||||
|
||||
def preds contester
|
||||
perc = Prediction.count(:conditions => ["match_id = ? AND score#{contester} > 2", id])
|
||||
perc != 0 ? (perc/predictions.count.to_f*100).round : 0
|
||||
end
|
||||
|
||||
def mercs contester
|
||||
matchers.all :conditions => {:merc => true, :contester_id => contester.id}
|
||||
end
|
||||
|
||||
def get_hltv
|
||||
self.hltv = hltv ? hltv : Server.hltvs.active.unreserved_hltv_around(match_time).first
|
||||
end
|
||||
|
||||
def demo_name
|
||||
Verification.uncrap(contest.short_name + "-" + self.id.to_s + "_" + contester1.to_s + "-vs-" + contester2.to_s)
|
||||
end
|
||||
|
||||
def team1_lineup
|
||||
matchers.all(:conditions => {:contester_id => contester1_id}).collect{|matcher| matcher.user.username} * ", "
|
||||
end
|
||||
|
||||
def team2_lineup
|
||||
matchers.all(:conditions => {:contester_id => contester2_id}).collect{|matcher| matcher.user.username} * ", "
|
||||
end
|
||||
|
||||
def get_friendly param = nil
|
||||
if param.nil?
|
||||
friendly == contester1.team ? contester1 : contester2
|
||||
elsif param == :score
|
||||
friendly == contester1.team ? score1 : score2
|
||||
elsif param == :points
|
||||
friendly == contester1.team ? points1 : points2
|
||||
end
|
||||
end
|
||||
|
||||
def get_opponent param = nil
|
||||
if param.nil?
|
||||
friendly == contester1.team ? contester2 : contester1
|
||||
elsif param == :score
|
||||
friendly == contester1.team ? score2 : score1
|
||||
elsif param == :points
|
||||
friendly == contester1.team ? points2 : points1
|
||||
end
|
||||
end
|
||||
|
||||
def set_hltv
|
||||
get_hltv if match_time.future?
|
||||
end
|
||||
|
||||
def send_notifications
|
||||
Profile.all(:include => :user, :conditions => "notify_any_match = 1").each do |p|
|
||||
Notifications.match p.user, self if p.user
|
||||
end
|
||||
contester2.team.teamers.active.each do |teamer|
|
||||
if teamer.user.profile.notify_own_match
|
||||
Notifications.challenge teamer.user, self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_motm
|
||||
self.motm = User.find_by_username(motm_name)
|
||||
end
|
||||
|
||||
def set_predictions
|
||||
predictions.update_all "result = 0"
|
||||
predictions.update_all "result = 1", ["score1 = ? AND score2 = ?", score1, score2]
|
||||
end
|
||||
|
||||
def after_destroy
|
||||
predictions.update_all "result = 0"
|
||||
contest.recalculate
|
||||
end
|
||||
|
||||
def recalculate
|
||||
return if score1.nil? or score2.nil?
|
||||
return if contest.contest_type == Contest::TYPE_LEAGUE and !contester2.active or !contester1.active
|
||||
|
||||
if score1 == score2
|
||||
contester1.draw = contester1.draw + 1
|
||||
contester2.draw = contester2.draw + 1
|
||||
contester1.trend = Contester::TREND_FLAT
|
||||
contester2.trend = Contester::TREND_FLAT
|
||||
elsif score1 > score2
|
||||
contester1.win = contester1.win + 1
|
||||
contester2.loss = contester2.loss + 1
|
||||
contester1.trend = Contester::TREND_UP
|
||||
contester2.trend = Contester::TREND_DOWN
|
||||
elsif score1 < score2
|
||||
contester1.loss = contester1.loss + 1
|
||||
contester2.win = contester2.win + 1
|
||||
contester1.trend = Contester::TREND_DOWN
|
||||
contester2.trend = Contester::TREND_UP
|
||||
end
|
||||
|
||||
self.diff = diff ? diff : (contester2.score-contester1.score)
|
||||
|
||||
if contest.contest_type == Contest::TYPE_LADDER
|
||||
self.points1 = contest.elo_score score1, score2, diff
|
||||
self.points2 = contest.elo_score score2, score1, -(diff)
|
||||
elsif contest.contest_type == Contest::TYPE_LEAGUE
|
||||
self.points1 = score1
|
||||
self.points2 = score2
|
||||
end
|
||||
|
||||
if contest.contest_type == Contest::TYPE_LADDER
|
||||
contester1.extra = contester1.extra + contest.modulus_base/10
|
||||
contester2.extra = contester2.extra + contest.modulus_base/10
|
||||
end
|
||||
|
||||
unless contest.contest_type == Contest::TYPE_BRACKET
|
||||
contester1.score = contester1.score+points1 < 0 ? 0 : contester1.score+points1
|
||||
contester2.score = contester2.score+points2 < 0 ? 0 : contester2.score+points2
|
||||
contester1.save!
|
||||
contester2.save!
|
||||
end
|
||||
end
|
||||
|
||||
def hltv_record addr, pwd
|
||||
if (match_time - MATCH_LENGTH*10) > DateTime.now or (match_time + MATCH_LENGTH*10) < DateTime.now
|
||||
raise Error, I18n.t(:hltv_request_20)
|
||||
end
|
||||
if hltv and hltv.recording
|
||||
raise Error, I18n.t(:hltv_already) + hltv.addr
|
||||
end
|
||||
unless get_hltv
|
||||
raise Error, I18n.t(:hltv_notavailable)
|
||||
end
|
||||
|
||||
save!
|
||||
hltv.reservation = addr
|
||||
hltv.pwd = pwd
|
||||
hltv.recordable = self
|
||||
hltv.save!
|
||||
end
|
||||
|
||||
def hltv_move addr, pwd
|
||||
raise Error, I18n.t(:hltv_notset) if hltv.nil? or hltv.recording.nil?
|
||||
Server.move hltv.reservation, addr, pwd
|
||||
end
|
||||
|
||||
def hltv_stop
|
||||
raise Error, I18n.t(:hltv_notset) if hltv.nil? or hltv.recording.nil?
|
||||
Server.stop hltv.reservation
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser, params = {}
|
||||
return false unless cuser
|
||||
return true if cuser.admin?
|
||||
return true if cuser.ref? and !referee and Verification.contain params, [:referee_id]
|
||||
return true if cuser.ref? and referee == cuser \
|
||||
and Verification.contain params, [:score1, :score2, :forfeit, :report, :demo_id, :motm_name, :matchers_attributes, :server_id]
|
||||
return true if match_time.past? and !score1 and !score2 and !forfeit \
|
||||
and Verification.contain(params, [:score1, :score2]) \
|
||||
and (contester1.team.is_leader? cuser or contester2.team.is_leader? cuser)
|
||||
return true if match_time.today? \
|
||||
and Verification.contain(params, [:stream_id]) \
|
||||
and (contester1.team.is_leader? cuser or contester2.team.is_leader? cuser)
|
||||
return true if match_time.past? \
|
||||
and Verification.contain(params, [:matchers_attributes]) \
|
||||
and (contester1.team.is_leader? cuser or contester2.team.is_leader? cuser)
|
||||
return true if (cuser.ref? and referee == cuser) \
|
||||
and Verification.contain(params, [:hltv]) \
|
||||
and !demo
|
||||
false
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
25
app/models/matcher.rb
Normal file
25
app/models/matcher.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
class Matcher < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
|
||||
belongs_to :match
|
||||
belongs_to :user
|
||||
belongs_to :contester
|
||||
has_many :teams, :through => :contester
|
||||
|
||||
scope :stats,
|
||||
:select => "user_id, COUNT(*) as num, users.username",
|
||||
:joins => "LEFT JOIN users ON users.id = user_id",
|
||||
:group => "user_id",
|
||||
:having => "num > 20",
|
||||
:order => "num DESC"
|
||||
scope :mercs, :conditions => {:merc => true}
|
||||
scope :of_contester,
|
||||
lambda { |contester| {:conditions => {:contester_id => contester.id}} }
|
||||
|
||||
validates_presence_of :match, :user
|
||||
validates_uniqueness_of :user_id, :scope => :match_id
|
||||
validates_inclusion_of :merc, :in => [true, false]
|
||||
|
||||
end
|
71
app/models/message.rb
Normal file
71
app/models/message.rb
Normal file
|
@ -0,0 +1,71 @@
|
|||
require 'rbbcode'
|
||||
|
||||
class Message < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :created_at, :updated_at
|
||||
attr_accessor :sender_raw
|
||||
|
||||
validates_length_of :title, :in => 1..100
|
||||
validates_length_of :text, :in => 1..65000
|
||||
|
||||
scope :ordered, :order => "created_at DESC"
|
||||
scope :read_by,
|
||||
lambda { |user| {:include => :readings, :conditions => ["readings.user_id = ?", user.id]} }
|
||||
scope :unread_by,
|
||||
lambda { |user| {
|
||||
:joins => "LEFT JOIN readings ON readable_type = 'Message' AND readable_id = messages.id AND readings.user_id = #{user.id}",
|
||||
:conditions => "readings.user_id IS NULL"} }
|
||||
|
||||
belongs_to :sender, :polymorphic => true
|
||||
belongs_to :recipient, :polymorphic => true
|
||||
|
||||
before_save :parse_text
|
||||
after_create :send_notifications
|
||||
|
||||
acts_as_readable
|
||||
|
||||
def to_s
|
||||
title
|
||||
end
|
||||
|
||||
def thread
|
||||
Message.find_by_sql ["
|
||||
(SELECT `messages`.* FROM `messages` WHERE `messages`.`sender_id` = ? AND `messages`.`sender_type` = 'User' AND `messages`.`recipient_id` = ?)
|
||||
UNION
|
||||
(SELECT `messages`.* FROM `messages` WHERE `messages`.`sender_id` = ? AND `messages`.`sender_type` = 'User' AND `messages`.`recipient_id` = ?)
|
||||
ORDER BY id", sender.id, recipient.id, recipient.id, sender.id]
|
||||
end
|
||||
|
||||
def parse_text
|
||||
self.text_parsed = RbbCode::Parser.new.parse(text)
|
||||
end
|
||||
|
||||
def send_notifications
|
||||
if recipient.instance_of?(User)
|
||||
if recipient.profile.notify_pms
|
||||
Notifications.pm recipient, self
|
||||
end
|
||||
elsif recipient.instance_of?(Group)
|
||||
recipient.users.each do |u|
|
||||
if u.profile.notify_pms
|
||||
Notifications.pm u, self
|
||||
end
|
||||
end
|
||||
elsif recipient.instance_of?(Team)
|
||||
recipient.teamers.active.each do |teamer|
|
||||
if teamer.user.profile.notify_pms
|
||||
Notifications.pm teamer.user, self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_show? cuser
|
||||
cuser and (cuser.received_messages.include?(self) or cuser.sent_messages.include?(self))
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and !cuser.banned?(Ban::TYPE_MUTE)
|
||||
end
|
||||
end
|
135
app/models/movie.rb
Normal file
135
app/models/movie.rb
Normal file
|
@ -0,0 +1,135 @@
|
|||
require 'data_file'
|
||||
|
||||
class Movie < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
MOVIES = "movies"
|
||||
FFMPEG = "/usr/local/bin/ffmpeg"
|
||||
SCREEN = "/usr/bin/screen"
|
||||
VLC = "/usr/bin/vlc"
|
||||
LOCAL = "78.46.36.107:29100"
|
||||
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
attr_accessor :user_name, :name, :stream_ip, :stream_port
|
||||
|
||||
scope :recent, :limit => 5
|
||||
scope :ordered,
|
||||
:include => "file",
|
||||
:order => "data_files.created_at DESC"
|
||||
scope :index,
|
||||
lambda { |order| {
|
||||
:select => "movies.*, users.username, AVG(rates.score) as total_ratings",
|
||||
:joins => "LEFT JOIN data_files ON movies.file_id = data_files.id
|
||||
LEFT JOIN users ON movies.user_id = users.id
|
||||
LEFT JOIN ratings ON rateable_id = data_files.id AND rateable_type = 'DataFile'
|
||||
LEFT JOIN rates ON ratings.rate_id = rates.id",
|
||||
:order => order,
|
||||
:group => "movies.id"} }
|
||||
scope :active_streams, :conditions => "status > 0"
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :file, :class_name => "DataFile"
|
||||
belongs_to :preview, :class_name => "DataFile"
|
||||
belongs_to :match
|
||||
belongs_to :category
|
||||
has_many :ratings, :as => :rateable
|
||||
has_many :shoutmsgs, :as => :shoutable
|
||||
has_many :watchers
|
||||
has_many :watcher_users, :through => :watchers, :source => :user
|
||||
|
||||
validates_length_of [:content, :format], :maximum => 100, :allow_blank => true
|
||||
validates_inclusion_of :length, :in => 0..50000, :allow_blank => true, :allow_nil => true
|
||||
validates_presence_of :file
|
||||
|
||||
mount_uploader :picture, MovieUploader
|
||||
|
||||
has_view_count
|
||||
acts_as_readable
|
||||
|
||||
def to_s
|
||||
file.to_s
|
||||
end
|
||||
|
||||
def length_s
|
||||
(length/60).to_s + ":" + (length%60).to_s if length
|
||||
end
|
||||
|
||||
def get_user
|
||||
user_id ? User.find(user_id) : ""
|
||||
end
|
||||
|
||||
def get_length
|
||||
if m = (`exiftool -Duration "#{file.full_path}" 2> /dev/null | grep -o '[0-9]*:[0-9]*' | tail -n1`.match(/([0-9]*):([0-9]*)/))
|
||||
update_attribute :length, m[1].to_i*60 + m[2].to_i
|
||||
end
|
||||
end
|
||||
|
||||
def all_files
|
||||
file ? ([file] + file.related_files) : []
|
||||
end
|
||||
|
||||
def before_validation
|
||||
if user_name and !user_name.empty?
|
||||
self.user = User.find_by_username(user_name)
|
||||
else
|
||||
self.user = nil
|
||||
end
|
||||
#if file.nil? and match and stream_ip and stream_port
|
||||
# build_file
|
||||
# self.file.directory = Directory.find(Directory::MOVIES)
|
||||
# self.file.path = File.join(self.file.directory.path, match.demo_name + ".mp4")
|
||||
# self.file.description = match.contest.short_name + ": " + match.contester1.to_s + " vs " + match.contester2.to_s
|
||||
# FileUtils.touch(self.file.path)
|
||||
# self.file.save!
|
||||
# make_stream
|
||||
#end
|
||||
end
|
||||
|
||||
def make_preview x, y
|
||||
result = file.full_path.gsub(/\.\w{3}$/, "") + "_preview.mp4"
|
||||
params = "-vcodec libx264 -vpre hq -b 1200k -bt 1200k -acodec libmp3lame -ab 128k -ac 2"
|
||||
cmd = "#{SCREEN} -d -m #{FFMPEG} -y -i \"#{file.full_path}\" #{params} \"#{result}\""
|
||||
system cmd
|
||||
cmd
|
||||
end
|
||||
|
||||
def make_snapshot secs
|
||||
image = File.join(Rails.root, "public", "images", MOVIES, id.to_s + ".png")
|
||||
params = "-ss #{secs} -vcodec png -vframes 1 -an -f rawvideo -s 160x120"
|
||||
Movie.update_all({:picture => "#{id}.png"}, {:id => id})
|
||||
cmd = "#{FFMPEG} -y -i \"#{file.full_path}\" #{params} \"#{image}\""
|
||||
system cmd
|
||||
cmd
|
||||
end
|
||||
|
||||
def make_stream
|
||||
ip = stream_ip.match(/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/)[0]
|
||||
port = stream_port.match(/[0-9]{1,5}/)[0]
|
||||
cmd = "#{SCREEN} -d -m #{VLC} http://#{ip}:#{port} --sout \"#duplicate{dst=std{access=file,mux=mp4,dst=#{file.full_path}},dst=std{access=http,mux=ts,dst=#{LOCAL}}}\" vlc://quit"
|
||||
system cmd
|
||||
update_attribute :status, $?.pid
|
||||
cmd
|
||||
end
|
||||
|
||||
#def update_status
|
||||
# if status and status > 0
|
||||
# begin
|
||||
# Process.getpgid(status) != -1
|
||||
# rescue Errno::ESRCH
|
||||
# update_attribute :status, 0
|
||||
# end
|
||||
# end
|
||||
#end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin? or cuser.groups.exists? :id => Group::MOVIES
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin? or user == cuser
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
14
app/models/option.rb
Normal file
14
app/models/option.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
class Option < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :votes
|
||||
|
||||
validates_length_of :option, :in => 1..30
|
||||
|
||||
has_many :real_votes, :class_name => "Vote", :as => :votable
|
||||
belongs_to :poll
|
||||
|
||||
def to_s
|
||||
self.option
|
||||
end
|
||||
end
|
30
app/models/poll.rb
Normal file
30
app/models/poll.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
class Poll < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :votes, :user_id
|
||||
|
||||
validates_length_of :question, :in => 1..50
|
||||
#validates_datetime :end_date
|
||||
|
||||
belongs_to :user
|
||||
has_many :options, :class_name => "Option", :dependent => :destroy
|
||||
has_many :real_votes, :through => :options
|
||||
|
||||
accepts_nested_attributes_for :options, :allow_destroy => true
|
||||
|
||||
def voted? user
|
||||
real_votes.count(:conditions => {:user_id => user.id}) > 0
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
80
app/models/post.rb
Normal file
80
app/models/post.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
require 'rbbcode'
|
||||
require 'bb-ruby'
|
||||
|
||||
class Post < ActiveRecord::Base
|
||||
BBCODE_DEFAULTS = {
|
||||
'Quote' => [
|
||||
/\[quote(:.*)?=(.*?)\](.*?)\[\/quote\1?\]/mi,
|
||||
'<fieldset class="quote"><legend>\2</legend>\3</fieldset>',
|
||||
'Quote with citation',
|
||||
'[quote=mike]please quote me[/quote]',
|
||||
:quote
|
||||
],
|
||||
'Quote without author' => [
|
||||
/\[quote(:.*)\](.*?)\[\/quote\1?\]/mi,
|
||||
'<fieldset class="quote">\2</fieldset>',
|
||||
'Quote without citation',
|
||||
'[quote]please quote me[/quote]',
|
||||
:quote
|
||||
],
|
||||
}
|
||||
|
||||
include Extra
|
||||
attr_protected :id, :updated_at, :created_at, :votes, :user_id
|
||||
|
||||
scope :basic, :include => [{:user => [:team, :profile]}, :topic]
|
||||
|
||||
validates_presence_of :topic, :user
|
||||
validates_length_of :text, :in => 1..10000
|
||||
|
||||
before_save :parse_text
|
||||
after_create :remove_readings
|
||||
after_destroy :remove_topics, :if => Proc.new {|post| post.topic.posts.count == 0}
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :topic
|
||||
|
||||
# acts_as_indexed :fields => [:text]
|
||||
|
||||
def number pages, i
|
||||
if i != -1
|
||||
pages.per_page * (pages.current_page - 1) + i + 1
|
||||
else
|
||||
topic.posts.count + 1
|
||||
end
|
||||
end
|
||||
|
||||
def remove_readings
|
||||
Reading.delete_all ["readable_type = 'Topic' AND readable_id = ?", topic.id]
|
||||
Reading.delete_all ["readable_type = 'Forum' AND readable_id = ?", topic.forum.id]
|
||||
end
|
||||
|
||||
def parse_text
|
||||
self.text_parsed = self.text.bbcode_to_html(BBCODE_DEFAULTS) if self.text
|
||||
end
|
||||
|
||||
def remove_topics
|
||||
topic.destroy
|
||||
end
|
||||
|
||||
def error_messages
|
||||
self.errors.full_messages.uniq
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
return false unless cuser
|
||||
errors.add :lock, I18n.t(:topics_locked) if topic.lock
|
||||
errors.add :user, I18n.t(:bans_mute) if cuser.banned?(Ban::TYPE_MUTE) and topic.forum != Forum::BANS
|
||||
errors.add :user, I18n.t(:registered_for_week) unless cuser.verified?
|
||||
(Forum.available_to(cuser, Forumer::ACCESS_REPLY).of_forum(topic.forum).first and errors.size == 0)
|
||||
end
|
||||
|
||||
def can_update? cuser, params = {}
|
||||
return false unless cuser
|
||||
true if Verification.contain(params, [:text, :topic_id]) and user == cuser or cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
19
app/models/prediction.rb
Normal file
19
app/models/prediction.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
class Prediction < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :created_at, :updated_at, :result
|
||||
|
||||
validates_presence_of :match, :user
|
||||
validates_inclusion_of :score1, :in => 0..4, :message => "Invalid score"
|
||||
validates_inclusion_of :score2, :in => 0..4, :message => "Invalid score"
|
||||
validates_uniqueness_of :match_id, :scope => :user_id
|
||||
|
||||
scope :with_contest, :include => {:match => :contest}
|
||||
|
||||
belongs_to :match
|
||||
belongs_to :user
|
||||
|
||||
def can_create? cuser
|
||||
cuser and match.match_time.future? and !match.score1 and !match.score2 and !cuser.predictions.exists?(:match_id => match.id)
|
||||
end
|
||||
end
|
42
app/models/profile.rb
Normal file
42
app/models/profile.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
require 'rbbcode'
|
||||
|
||||
class Profile < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :user_id, :id, :updated_at, :created_at
|
||||
|
||||
belongs_to :user
|
||||
|
||||
validates_length_of :msn, :maximum => 50
|
||||
validates_format_of :msn, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :allow_blank => true
|
||||
validates_format_of :icq, :with => /\A[0-9\-]{1,9}\z/, :allow_blank => true
|
||||
validates_length_of :irc, :maximum => 20
|
||||
validates_length_of :web, :maximum => 100
|
||||
validates_length_of :town, :maximum => 20
|
||||
validates_length_of [:singleplayer, :multiplayer, :food, :beverage, :hobby, :music, :book, :movie, :tvseries], :maximum => 120
|
||||
validates_length_of [:res, :sensitivity, :monitor_hz], :maximum => 30
|
||||
validates_length_of [:scripts, :cpu, :gpu, :ram, :psu, :motherboard, :soundcard, :hdd, :case, :monitor, :mouse, :mouse_pad, :keyboard, :head_phones, :speakers], :maximum => 100
|
||||
validates_length_of :signature, :maximum => 255
|
||||
validates_length_of :achievements, :maximum => 65000
|
||||
validates_format_of :steam_profile, :with => /\A[A-Za-z0-9_\-\+]{1,40}\z/, :allow_blank => true
|
||||
|
||||
before_validation :init_steam_profile
|
||||
before_save :parse_text
|
||||
|
||||
mount_uploader :avatar, AvatarUploader
|
||||
|
||||
def init_steam_profile
|
||||
if steam_profile
|
||||
if (m = steam_profile.to_s.match(/http:\/\/steamcommunity\.com\/profiles\/([0-9]*)/))
|
||||
self.steam_profile = m[1]
|
||||
elsif (m = steam_profile.to_s.match(/http:\/\/steamcommunity\.com\/id\/([A-Za-z0-9_\-\+]*)/))
|
||||
self.steam_profile = m[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def parse_text
|
||||
self.achievements_parsed = RbbCode::Parser.new.parse(achievements) if self.achievements
|
||||
self.signature_parsed = RbbCode::Parser.new.parse(signature) if self.signature
|
||||
end
|
||||
end
|
111
app/models/round.rb
Normal file
111
app/models/round.rb
Normal file
|
@ -0,0 +1,111 @@
|
|||
class Round < ActiveRecord::Base
|
||||
scope :basic, :include => [:commander, :map, :server, :team1, :team2], :order => "start DESC"
|
||||
scope :team_stats,
|
||||
:select => "team1_id AS team_id, COUNT(*) as rounds, AVG(winner) AS wlr, teams.name",
|
||||
:joins => "LEFT JOIN teams ON teams.id = team1_id",
|
||||
:group => "team_id",
|
||||
:order => "rounds DESC",
|
||||
:having => "rounds > 10 AND team_id IS NOT NULL",
|
||||
:limit => 100
|
||||
|
||||
has_many :rounders, :dependent => :destroy
|
||||
has_many :logs, :dependent => :destroy
|
||||
|
||||
belongs_to :team1, :class_name => "Team"
|
||||
belongs_to :team2, :class_name => "Team"
|
||||
belongs_to :server
|
||||
belongs_to :match
|
||||
belongs_to :commander, :class_name => "Rounder"
|
||||
belongs_to :map
|
||||
|
||||
def length
|
||||
sprintf("%02d:%02d", (self.end - self.start).to_i/60, (self.end - self.start).to_i%60)
|
||||
end
|
||||
|
||||
def winner_s
|
||||
winner == 1 ? "Marines" : "Aliens"
|
||||
end
|
||||
|
||||
def marine_stats
|
||||
{"built_resourcetower" => "Marine RTs",
|
||||
"built_item_genericammo" => "Ammo Packs",
|
||||
"built_item_health" => "Medpacks"}
|
||||
end
|
||||
|
||||
def alien_stats
|
||||
{"built_alienresourcetower" => "Alien RTs",
|
||||
"lerk" => "Lerks",
|
||||
"fade" => "Fades",
|
||||
"onos" => "Onoses"}
|
||||
end
|
||||
|
||||
def self.marine_events
|
||||
{"built_resourcetower" => "RT Built",
|
||||
"built_phasegate" => "PG Built",
|
||||
"built_team_infportal" => "IP Built",
|
||||
"built_team_armory" => "Armory Built",
|
||||
"built_team_observatory" => "Obs Built",
|
||||
"built_team_armslab" => "ARMS Built",
|
||||
"built_team_command" => "CC Built",
|
||||
"built_team_prototypelab" => "Proto Built",
|
||||
"built_team_turretfactory" => "TF Built",
|
||||
"built_team_turret" => "Turret Built",
|
||||
"built_team_siegeturret" => "Siege Built",
|
||||
"destroyed_resourcetower" => "RT Destroyed",
|
||||
"destroyed_phasegate" => "PG Destroyed",
|
||||
"destroyed_team_infportal" => "IP Destroyed",
|
||||
"destroyed_team_armory" => "Armory Destroyed",
|
||||
"destroyed_team_observatory" => "Obs Destroyed",
|
||||
"destroyed_team_armslab" => "ARMS Destroyed",
|
||||
"destroyed_team_command" => "CC Destroyed",
|
||||
"destroyed_team_prototypelab" => "Proto Destroyed",
|
||||
"destroyed_team_turretfactory" => "TF Destroyed",
|
||||
"destroyed_team_turret" => "Turret Destroyed",
|
||||
"destroyed_team_siegeturret" => "Siege Destroyed",
|
||||
"scan" => "Scan",
|
||||
"research_motiontracking" => "MT Tech",
|
||||
"research_phasetech" => "PG Tech",
|
||||
"research_distressbeacon" => "Beacon",
|
||||
"research_armorl1" => "Armor 1",
|
||||
"research_armorl2" => "Armor 2",
|
||||
"research_armorl3" => "Armor 3",
|
||||
"research_weaponsl1" => "Weapons 1",
|
||||
"research_weaponsl2" => "Weapons 2",
|
||||
"research_weaponsl3" => "Weapons 3",
|
||||
"research_advarmory" => "AA",
|
||||
"research_electrical" => "Electrify",
|
||||
"research_advturretfactory" => "A-TFAC",
|
||||
"research_cancel" => "Research Cancel",
|
||||
"kill" => "Frag"}
|
||||
end
|
||||
|
||||
def self.alien_events
|
||||
{"built_team_hive" => "Hive Built",
|
||||
"built_movementchamber" => "MC Built",
|
||||
"built_sensorychamber" => "SC Built",
|
||||
"built_defensechamber" => "DC Built",
|
||||
"built_offensechamber" => "OC Built",
|
||||
"built_alienresourcetower" => "RT Built",
|
||||
"destroyed_team_hive" => "Hive Destroyed",
|
||||
"destroyed_movementchamber" => "MC Destroyed",
|
||||
"destroyed_sensorychamber" => "SC Destroyed",
|
||||
"destroyed_defensechamber" => "DC Destroyed",
|
||||
"destroyed_offensechamber" => "OC Destroyed",
|
||||
"destroyed_alienresourcetower" => "RT Destroyed",
|
||||
"gorge" => "Gorge up",
|
||||
"lerk" => "Lerk up",
|
||||
"fade" => "Fade up",
|
||||
"onos" => "Onos up",
|
||||
"kill" => "Frag"}
|
||||
end
|
||||
|
||||
def self.alien_event event
|
||||
return alien_events[event] if alien_events.include?(event)
|
||||
return nil
|
||||
end
|
||||
|
||||
def self.marine_event event
|
||||
return marine_events[event] if marine_events.include?(event)
|
||||
return nil
|
||||
end
|
||||
end
|
36
app/models/rounder.rb
Normal file
36
app/models/rounder.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
class Rounder < ActiveRecord::Base
|
||||
attr_accessor :lifeform
|
||||
|
||||
scope :team, lambda { |team| {:conditions => {:team => team}} }
|
||||
scope :match, lambda { |steamid| {:conditions => {:steamid => steamid}} }
|
||||
scope :ordered, :order => "kills DESC, deaths ASC"
|
||||
scope :stats,
|
||||
:select => "id, team_id, COUNT(*) as num",
|
||||
:group => "team_id",
|
||||
:order => "num DESC",
|
||||
:having => "num > 3"
|
||||
scope :player_stats,
|
||||
:select => "id, user_id, SUM(kills)/SUM(deaths) as kpd, COUNT(*) as rounds",
|
||||
:group => "user_id",
|
||||
:order => "kpd DESC",
|
||||
:having => "rounds > 30 AND kpd > 0 AND user_id IS NOT NULL",
|
||||
:limit => 100
|
||||
scope :team_stats,
|
||||
:select => "id, team_id, SUM(kills)/SUM(deaths) as kpd, COUNT(DISTINCT round_id) as rounds",
|
||||
:group => "team_id",
|
||||
:order => "kpd DESC",
|
||||
:having => "rounds > 30 AND kpd > 0 AND team_id IS NOT NULL",
|
||||
:limit => 100
|
||||
scope :extras, :include => [:round, :user]
|
||||
scope :within,
|
||||
lambda { |from, to|
|
||||
{:conditions => ["created_at > ? AND created_at < ?", from.utc, to.utc]} }
|
||||
|
||||
belongs_to :round
|
||||
belongs_to :user
|
||||
belongs_to :ensl_team, :class_name => "Team", :foreign_key => "team_id"
|
||||
|
||||
def to_s
|
||||
user ? user.username : name
|
||||
end
|
||||
end
|
338
app/models/server.rb
Normal file
338
app/models/server.rb
Normal file
|
@ -0,0 +1,338 @@
|
|||
require "rcon"
|
||||
require "yaml"
|
||||
|
||||
class Server < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
DOMAIN_HLDS = 0
|
||||
DOMAIN_HLTV = 1
|
||||
DOMAIN_NS2 = 2
|
||||
HLTV_IDLE = 1200
|
||||
DEMOS = "/var/www/virtual/ensl.org/hlds_l/ns/demos"
|
||||
QSTAT = "/usr/bin/quakestat"
|
||||
TMPFILE = "tmp/server.txt"
|
||||
|
||||
attr_accessor :rcon_handle, :pwd
|
||||
attr_protected :id, :user_id, :updated_at, :created_at, :map, :players, :maxplayers, :ping, :version
|
||||
|
||||
validates_length_of [:name, :dns,], :in => 1..30
|
||||
validates_length_of [:rcon, :password, :irc], :maximum => 30, :allow_blank => true
|
||||
validates_length_of :description, :maximum => 255, :allow_blank => true
|
||||
validates_format_of :ip, :with => /\A[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\z/
|
||||
validates_format_of :port, :with => /\A[0-9]{1,5}\z/
|
||||
validates_format_of :reservation, :with => /\A[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5}\z/, :allow_nil => true
|
||||
validates_format_of :pwd, :with => /\A[A-Za-z0-9_\-]*\z/, :allow_nil => true
|
||||
|
||||
scope :ordered, :order => "name"
|
||||
scope :hlds, :conditions => ["domain = ?", DOMAIN_HLDS]
|
||||
scope :ns2, :conditions => ["domain = ?", DOMAIN_NS2]
|
||||
scope :hltvs, :conditions => ["domain = ?", DOMAIN_HLTV]
|
||||
scope :active, :conditions => "active = 1"
|
||||
scope :with_players, :conditions => "players > 0"
|
||||
scope :reserved, :conditions => "reservation IS NOT NULL"
|
||||
scope :unreserved_now, :conditions => "reservation IS NULL"
|
||||
scope :unreserved_hltv_around,
|
||||
lambda { |time| {
|
||||
:select => "servers.*",
|
||||
:joins => "LEFT JOIN matches ON servers.id = matches.hltv_id
|
||||
AND match_time > '#{(time.ago(Match::MATCH_LENGTH).utc).strftime("%Y-%m-%d %H:%M:%S")}'
|
||||
AND match_time < '#{(time.ago(-Match::MATCH_LENGTH).utc).strftime("%Y-%m-%d %H:%M:%S")}'",
|
||||
:conditions => "matches.hltv_id IS NULL"} }
|
||||
scope :of_addr,
|
||||
lambda { |addr| {
|
||||
:conditions => {
|
||||
:ip => addr.match(/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/)[0],
|
||||
:port => addr.match(/:([0-9]{1,5})/)[1] } } }
|
||||
scope :of_category,
|
||||
lambda { |category| {
|
||||
:conditions => {:category_id => category.id} }}
|
||||
|
||||
has_many :logs
|
||||
has_many :matches
|
||||
has_many :challenges
|
||||
belongs_to :user
|
||||
belongs_to :recordable, :polymorphic => true
|
||||
|
||||
before_create :set_category
|
||||
|
||||
acts_as_versioned
|
||||
non_versioned_columns << 'name'
|
||||
non_versioned_columns << 'description'
|
||||
non_versioned_columns << 'dns'
|
||||
non_versioned_columns << 'ip'
|
||||
non_versioned_columns << 'port'
|
||||
non_versioned_columns << 'rcon'
|
||||
non_versioned_columns << 'password'
|
||||
non_versioned_columns << 'irc'
|
||||
non_versioned_columns << 'user_id'
|
||||
non_versioned_columns << 'official'
|
||||
non_versioned_columns << 'domain'
|
||||
non_versioned_columns << 'reservation'
|
||||
non_versioned_columns << 'recording'
|
||||
non_versioned_columns << 'idle'
|
||||
non_versioned_columns << 'default_id'
|
||||
non_versioned_columns << 'active'
|
||||
non_versioned_columns << 'recordable_type'
|
||||
non_versioned_columns << 'recordable_id'
|
||||
|
||||
def domains
|
||||
{DOMAIN_HLTV => "HLTV", DOMAIN_HLDS => "NS Server", DOMAIN_NS2 => "NS2 Server"}
|
||||
end
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def addr
|
||||
ip + ":" + port.to_s
|
||||
end
|
||||
|
||||
def players_s
|
||||
if players.nil? or max_players.nil?
|
||||
"N/A"
|
||||
else
|
||||
players.to_s + " / " + max_players.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def recording_s
|
||||
return nil if self.domain != DOMAIN_HLTV
|
||||
# recording.to_i > 0 ? Match.find(recording).to_s : recording
|
||||
recording
|
||||
end
|
||||
|
||||
def reservation_s
|
||||
return nil if domain != DOMAIN_HLTV
|
||||
reservation
|
||||
end
|
||||
|
||||
def graphfile
|
||||
File.join("public", "images", "servers", id.to_s + ".png")
|
||||
end
|
||||
|
||||
def set_category
|
||||
self.category_id = (domain == DOMAIN_NS2 ? 45 : 44 )
|
||||
end
|
||||
|
||||
def after_validation_on_update
|
||||
if reservation_changed?
|
||||
rcon_connect
|
||||
if reservation == nil
|
||||
rcon_exec "stop"
|
||||
self.recording = nil
|
||||
self.recordable = nil
|
||||
self.idle = nil
|
||||
save_demos if self.recording
|
||||
hltv_stop
|
||||
else
|
||||
if changes['reservation'][0].nil?
|
||||
hltv_start
|
||||
rcon_exec "stop"
|
||||
self.recording = recordable.demo_name if recordable and recordable_type == "Match" or recordable_type == "Gather"
|
||||
rcon_exec "record demos/" + self.recording if self.recording
|
||||
end
|
||||
rcon_exec "serverpassword " + pwd
|
||||
rcon_exec "connect " + reservation
|
||||
self.idle = DateTime.now
|
||||
end
|
||||
rcon_disconnect
|
||||
end
|
||||
end
|
||||
|
||||
def execute command
|
||||
rcon_connect
|
||||
response = rcon_exec command
|
||||
rcon_disconnect
|
||||
response
|
||||
end
|
||||
|
||||
def rcon_connect
|
||||
self.rcon_handle = RCon::Query::Original.new(ip, port, rcon)
|
||||
end
|
||||
|
||||
def rcon_exec command
|
||||
response = rcon_handle.command(command)
|
||||
|
||||
Log.transaction do
|
||||
Log.add(self, Log::DOMAIN_RCON_COMMAND, command)
|
||||
if response.to_s.length > 0
|
||||
Log.add(self, Log::DOMAIN_RCON_RESPONSE, response)
|
||||
end
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
def rcon_disconnect
|
||||
rcon_handle.disconnect
|
||||
end
|
||||
|
||||
def hltv_start
|
||||
if nr = hltv_nr
|
||||
`screen -d -m -S "Hltv-#{nr[1]}" -c $HOME/.screenrc-hltv $HOME/hlds_l/hltv -ip 78.46.36.107 -port 28#{nr[1]}00 +exec ns/hltv#{nr[1]}.cfg`
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
|
||||
def hltv_nr
|
||||
self.name.match(/Tv \#([0-9])/)
|
||||
end
|
||||
|
||||
def hltv_stop
|
||||
if nr = hltv_nr
|
||||
sleep 5
|
||||
rcon_exec "exit"
|
||||
#`screen -S "Hltv-#{nr[1]}" -X 'quit'`
|
||||
end
|
||||
end
|
||||
|
||||
def save_demos
|
||||
dir = case recordable_type
|
||||
when "Match" then
|
||||
recordable.contest.demos
|
||||
when "Gather" then
|
||||
Directory.find(Directory::DEMOS_GATHERS)
|
||||
end
|
||||
|
||||
dir ||= Directory.find(Directory::DEMOS_DEFAULT)
|
||||
zip_path = File.join(dir.path, recording + ".zip")
|
||||
|
||||
Zip::ZipOutputStream::open(zip_path) do |zos|
|
||||
if recordable_type == "Match"
|
||||
zos.put_next_entry "readme.txt"
|
||||
zos.write "Team1: " + recordable.contester1.to_s + "\r\n"
|
||||
zos.write "Team2: " + recordable.contester2.to_s + "\r\n"
|
||||
zos.write "Date: " + recordable.match_time.to_s + "\r\n"
|
||||
zos.write "Contest: " + recordable.contest.to_s + "\r\n"
|
||||
zos.write "Server: " + recordable.server.addr + "\r\n" if recordable.server
|
||||
zos.write "HLTV: " + addr + "\r\n"
|
||||
zos.write YAML::dump(recordable.attributes).to_s
|
||||
end
|
||||
Dir.glob("#{DEMOS}/*").each do |file|
|
||||
if File.file?(file) and file.match(/#{recording}.*\.dem/)
|
||||
zos.put_next_entry File.basename(file)
|
||||
zos.write(IO.read(file))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DataFile.transaction do
|
||||
unless dbfile = DataFile.find_by_path(zip_path)
|
||||
dbfile = DataFile.new
|
||||
dbfile.path = zip_path
|
||||
dbfile.directory = dir
|
||||
dbfile.save!
|
||||
DataFile.update_all({:name => File.basename(zip_path)}, {:id => dbfile.id})
|
||||
end
|
||||
if recordable_type == "Match"
|
||||
recordable.demo = dbfile
|
||||
recordable.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def make_stats
|
||||
graph = Gruff::Line.new
|
||||
graph.title = name
|
||||
pings = []
|
||||
players = []
|
||||
labels = {}
|
||||
n = 0
|
||||
|
||||
for version in versions.all(:order => "updated_at DESC", :limit => 30).reverse
|
||||
pings << version.ping.to_i
|
||||
players << version.players.to_i
|
||||
labels[n] = version.updated_at.strftime("%H:%M") if n % 3 == 0
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
graph.theme_37signals
|
||||
graph.data("Ping", pings, '#057fc0')
|
||||
graph.data("Players", players, '#ff0000')
|
||||
graph.labels = labels
|
||||
graph.write(graphfile)
|
||||
end
|
||||
|
||||
def default_record
|
||||
# if self.default
|
||||
# rcon_exec "record demos/auto-" + Verification.uncrap(default.name)
|
||||
# rcon_exec "serverpassword " + default.password
|
||||
# rcon_exec "connect " + default.addr
|
||||
# end
|
||||
end
|
||||
|
||||
def is_free time
|
||||
challenges.around(time).pending.count == 0 and matches.around(time).count == 0
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin? or user == cuser
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def self.refresh
|
||||
servers = ""
|
||||
Server.hlds.active.all.each do |server|
|
||||
servers << " -a2s " + server.ip + ":" + server.port.to_s
|
||||
end
|
||||
|
||||
file = File.join(Rails.root, TMPFILE)
|
||||
system "#{QSTAT} -xml #{servers} | grep -v '<name>' > #{file}"
|
||||
|
||||
doc = REXML::Document.new(File.new(file).read)
|
||||
doc.elements.each('qstat/server') do |server|
|
||||
hostname = server.elements['hostname'].text.split(':', 2)
|
||||
if s = Server.active.first(:conditions => {:ip => hostname[0], :port => hostname[1]})
|
||||
if server.elements.include? 'map'
|
||||
s.map = server.elements['map'].text
|
||||
s.players = server.elements['numplayers'].text.to_i
|
||||
s.max_players = server.elements['maxplayers'].text.to_i
|
||||
s.ping = server.elements['ping'].text
|
||||
s.map = server.elements['map'].text
|
||||
s.save
|
||||
s.make_stats
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
servers = ""
|
||||
Server.hltvs.reserved.each do |server|
|
||||
servers << " -a2s #{server.reservation}"
|
||||
end
|
||||
|
||||
doc = REXML::Document.new(`#{QSTAT} -xml #{servers} | grep -v '<name>'`)
|
||||
doc.elements.each('qstat/server') do |server|
|
||||
hostname = server.elements['hostname'].text.split(':', 2)
|
||||
if s = Server.hltvs.reserved.first(:conditions => {:ip => hostname[0], :port => hostname[1]})
|
||||
if server.elements['numplayers'].text.to_i > 0
|
||||
s.update_attribute :idle, DateTime.now
|
||||
elsif (s.idle + HLTV_IDLE).past?
|
||||
s.reservation = nil
|
||||
s.save
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.move addr, newaddr, newpwd
|
||||
self.hltvs.all(:conditions => {:reservation => addr}).each do |hltv|
|
||||
hltv.reservation = newaddr
|
||||
hltv.pwd = newpwd
|
||||
hltv.save!
|
||||
end
|
||||
end
|
||||
|
||||
def self.stop addr
|
||||
self.hltvs.all(:conditions => {:reservation => addr}).each do |hltv|
|
||||
hltv.reservation = nil
|
||||
hltv.save!
|
||||
end
|
||||
end
|
||||
end
|
48
app/models/shoutmsg.rb
Normal file
48
app/models/shoutmsg.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
class Shoutmsg < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :created_at, :updated_at, :user_id
|
||||
|
||||
validates_length_of :text, :in => 1..100
|
||||
validates_presence_of :user
|
||||
|
||||
scope :recent,
|
||||
:include => :user,
|
||||
:order => "id DESC",
|
||||
:limit => 8
|
||||
scope :box,
|
||||
:conditions => "shoutable_type IS NULL AND shoutable_id IS NULL",
|
||||
:limit => 8
|
||||
scope :typebox,
|
||||
:conditions => "shoutable_type IS NULL AND shoutable_id IS NULL"
|
||||
scope :lastXXX,
|
||||
:include => :user,
|
||||
:order => "id DESC",
|
||||
:limit => 500
|
||||
scope :of_object,
|
||||
lambda { |object, id| {:conditions => {:shoutable_type => object, :shoutable_id => id}} }
|
||||
scope :ordered, :order => "id"
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :shoutable, :polymorphic => true
|
||||
|
||||
def domain
|
||||
self[:shoutable_type] ? "shout_#{shoutable_type}_#{shoutable_id}" : "shoutbox"
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and !user.banned?(Ban::TYPE_MUTE) and cuser.verified?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def self.flood? cuser, type = nil, id = nil
|
||||
return false if self.of_object(type, id).count < 3
|
||||
self.of_object(type, id).all(:order => "created_at DESC", :limit => 10).each do |msg|
|
||||
return false if cuser != msg.user
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
100
app/models/team.rb
Normal file
100
app/models/team.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
class Team < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
LOGOS = "logos"
|
||||
STATUS_INACTIVE = 0
|
||||
STATUS_ACTIVE = 1
|
||||
|
||||
attr_protected :id, :active, :founder_id, :created_at, :updated_at
|
||||
|
||||
validates_presence_of :name, :tag
|
||||
validates_length_of :name, :tag, :in => 2..20
|
||||
validates_length_of :irc, :maximum => 20, :allow_blank => true
|
||||
validates_length_of :web, :maximum => 50, :allow_blank => true
|
||||
validates_format_of :country, :with => /\A[A-Z]{2}\z$/, :allow_blank => true
|
||||
validates_length_of [:comment, :recruiting], :in => 0..75, :allow_blank => true
|
||||
|
||||
scope :with_teamers_num,
|
||||
lambda { |num| {
|
||||
:select => "teams.*, COUNT(T.id) AS teamers_num",
|
||||
:joins => "LEFT JOIN teamers T ON T.team_id = teams.id AND T.rank >= #{Teamer::RANK_MEMBER}",
|
||||
:group => "teams.id",
|
||||
:having => ["teamers_num >= ?", num]} }
|
||||
scope :with_teamers, :include => :teamers
|
||||
scope :active, :conditions => {:active => true}
|
||||
scope :inactive, :conditions => {:active => false}
|
||||
scope :ordered, :order => "name"
|
||||
scope :recruiting, :conditions => "recruiting IS NOT NULL AND recruiting != ''"
|
||||
|
||||
belongs_to :founder, :class_name => "User"
|
||||
has_many :teamers, :dependent => :destroy
|
||||
has_many :leaders, :class_name => "Teamer", :conditions => ["rank = ?", Teamer::RANK_LEADER]
|
||||
has_many :contesters, :dependent => :destroy
|
||||
has_many :contests, :through => :contesters, :conditions => {"contesters.active" => true}
|
||||
has_many :received_messages, :class_name => "Message", :as => "recipient"
|
||||
has_many :sent_messages, :class_name => "Message", :as => "sender"
|
||||
has_many :matches, :through => :contesters
|
||||
has_many :matches_finished, :through => :contesters, :source => :matches, :conditions => "(score1 != 0 OR score2 != 0)"
|
||||
has_many :matches_won, :through => :contesters, :source => :matches,
|
||||
:conditions => "((score1 > score2 AND contester1_id = contesters.id) OR (score2 > score1 AND contester2_id = contesters.id)) AND (score1 != 0 OR score2 != 0)"
|
||||
has_many :matches_lost, :through => :contesters, :source => :matches,
|
||||
:conditions => "((score1 < score2 AND contester1_id = contesters.id) OR (score2 < score1 AND contester2_id = contesters.id)) AND (score1 != 0 OR score2 != 0)"
|
||||
has_many :matches_draw, :through => :contesters, :source => :matches,
|
||||
:conditions => "(score1 = score2 AND score1 > 0) AND (score1 != 0 OR score2 != 0)"
|
||||
|
||||
mount_uploader :logo, TeamUploader
|
||||
|
||||
before_create :init_variables
|
||||
after_create :add_leader
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
def leaders_s
|
||||
leaders.join(", ")
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.active = true
|
||||
self.recruiting = nil
|
||||
end
|
||||
|
||||
def add_leader
|
||||
teamer = Teamer.new
|
||||
teamer.user = founder
|
||||
teamer.team = self
|
||||
teamer.rank = Teamer::RANK_LEADER
|
||||
teamer.save
|
||||
founder.update_attribute :team_id, self.id
|
||||
end
|
||||
|
||||
def destroy
|
||||
if matches.count > 0
|
||||
update_attribute :active, false
|
||||
teamers.update_all ["rank = ?", Teamer::RANK_REMOVED]
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def recover
|
||||
update_attribute :active, true
|
||||
end
|
||||
|
||||
def is_leader? user
|
||||
teamers.leaders.exists?(:user_id => user.id)
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and !cuser.banned?(Ban::TYPE_MUTE)
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and is_leader? cuser or cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
95
app/models/teamer.rb
Normal file
95
app/models/teamer.rb
Normal file
|
@ -0,0 +1,95 @@
|
|||
class Teamer < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
RANK_REMOVED = -2
|
||||
RANK_JOINER = -1
|
||||
RANK_MEMBER = 0
|
||||
RANK_DEPUTEE = 1
|
||||
RANK_LEADER = 2
|
||||
|
||||
attr_protected :id, :created_at, :updated_at, :version
|
||||
|
||||
validates_length_of :comment, :in => 0..15, :allow_blank => true
|
||||
validates_uniqueness_of :user_id, :scope => [:team_id, :rank]
|
||||
validates_presence_of :user, :team
|
||||
#validate_on_create:validate_team
|
||||
#validate_on_create:validate_contests
|
||||
validate :validate_team
|
||||
|
||||
scope :basic,
|
||||
:include => :user,
|
||||
:order => "rank DESC, created_at ASC"
|
||||
scope :past,
|
||||
:conditions => ["teamers.rank = ?", RANK_REMOVED]
|
||||
scope :joining,
|
||||
:conditions => ["teamers.rank = ?", RANK_JOINER]
|
||||
scope :present,
|
||||
:conditions => ["teamers.rank >= ?", RANK_JOINER]
|
||||
scope :active,
|
||||
:conditions => ["teamers.rank >= ?", RANK_MEMBER]
|
||||
scope :leaders,
|
||||
:conditions => ["teamers.rank >= ?", RANK_DEPUTEE]
|
||||
scope :of_team,
|
||||
lambda { |team| {:conditions => {"teamers.team_id" => team.id}} }
|
||||
scope :active_teams,
|
||||
:include => :team,
|
||||
:conditions => ["teams.active = ?", true]
|
||||
scope :distinct,
|
||||
:group => "user_id, team_id"
|
||||
scope :ordered,
|
||||
:order => "rank DESC, created_at ASC"
|
||||
scope :historic,
|
||||
lambda { |user, time|
|
||||
{:conditions => ["user_id = ? AND created_at < ? AND ((updated_at > ? AND rank = ?) OR rank >= ?)",
|
||||
user.id, time.utc, time.utc, RANK_REMOVED, RANK_MEMBER]} }
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :team
|
||||
has_many :other_teamers, :through => :user, :source => :teamers, :conditions => ["teamers.id != ?", object_id]
|
||||
has_many :contesters, :through => :team
|
||||
|
||||
before_create :init_variables
|
||||
|
||||
def to_s
|
||||
user.to_s
|
||||
end
|
||||
|
||||
def ranks
|
||||
{RANK_JOINER => "Joining", RANK_MEMBER => "Member", RANK_DEPUTEE => "Deputee", RANK_LEADER => "Leader"}
|
||||
end
|
||||
|
||||
def validate_team
|
||||
if user.teamers.of_team(team).present.count > 0
|
||||
errors.add :team, I18n.t(:teams_join_twice)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_contests
|
||||
# TODO
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.rank = RANK_JOINER unless self.rank
|
||||
end
|
||||
|
||||
def destroy
|
||||
user.update_attribute :team, nil if user.team == team
|
||||
if rank == Teamer::RANK_JOINER
|
||||
super
|
||||
else
|
||||
update_attribute :rank, Teamer::RANK_REMOVED
|
||||
end
|
||||
end
|
||||
|
||||
def can_create? cuser, params
|
||||
cuser and Verification.contain params, [:user_id, :team_id]
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and (user == cuser or team.is_leader? cuser or cuser.admin?)
|
||||
end
|
||||
end
|
84
app/models/topic.rb
Normal file
84
app/models/topic.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
class Topic < ActiveRecord::Base
|
||||
POSTS_PAGE = 30
|
||||
STATE_NORMAL = 0
|
||||
STATE_STICKY = 1
|
||||
LATEST_PER_PAGE = 5
|
||||
|
||||
RULES = 12
|
||||
|
||||
include Extra
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
attr_accessor :first_post
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :forum
|
||||
has_one :lock, :as => :lockable
|
||||
has_one :latest, :class_name => "Post", :order => "id DESC"
|
||||
has_many :posts, :order => "id ASC", :dependent => :destroy
|
||||
has_many :view_counts, :as => :viewable, :dependent => :destroy
|
||||
|
||||
scope :basic,
|
||||
:include => [:latest, {:forum => :forumer}, :user]
|
||||
scope :ordered, :order => "state DESC, posts.id DESC"
|
||||
scope :recent,
|
||||
:conditions => "forumers.id IS NULL AND posts.id = (SELECT id FROM posts AS P WHERE P.topic_id = topics.id ORDER BY id DESC LIMIT 1)",
|
||||
:order => "posts.id DESC",
|
||||
:group => "topics.id"
|
||||
scope :latest_page,
|
||||
lambda { |page| {:limit => "#{(page-1)*LATEST_PER_PAGE}, #{(page-1)*LATEST_PER_PAGE+LATEST_PER_PAGE}"} }
|
||||
|
||||
validates_presence_of :user_id, :forum_id
|
||||
validates_length_of :title, :in => 1..50
|
||||
validates_length_of :first_post, :in => 1..10000, :on => :create
|
||||
|
||||
after_create :make_post
|
||||
|
||||
acts_as_readable
|
||||
|
||||
def to_s
|
||||
title
|
||||
end
|
||||
|
||||
def record_view_count(ip_address, logged_in = false)
|
||||
self.view_counts.create(:viewable => self, :ip_address => ip_address, :logged_in => logged_in)
|
||||
self
|
||||
end
|
||||
|
||||
def view_count
|
||||
self.view_counts.length
|
||||
end
|
||||
|
||||
def make_post
|
||||
c = posts.build
|
||||
c.text = first_post
|
||||
c.user = user
|
||||
c.save!
|
||||
end
|
||||
|
||||
def can_show? cuser
|
||||
forum.can_show? cuser
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
return false unless cuser
|
||||
errors.add :bans, I18n.t(:bans_mute) if cuser.banned?(Ban::TYPE_MUTE) and forum != Forum::BANS
|
||||
errors.add :bans, I18n.t(:registered_for_week) unless cuser.verified?
|
||||
(Forum.available_to(cuser, Forumer::ACCESS_TOPIC).of_forum(forum).first and errors.size == 0)
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def last_page
|
||||
[((posts.count - 1) / POSTS_PAGE) + 1, 1].max
|
||||
end
|
||||
|
||||
def states
|
||||
{STATE_NORMAL => "Normal", STATE_STICKY => "Sticky"}
|
||||
end
|
||||
end
|
11
app/models/tweet.rb
Normal file
11
app/models/tweet.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Tweet < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
scope :recent2, :order => "created_at DESC", :limit => 2
|
||||
scope :recent, :order => "created_at DESC", :limit => 5
|
||||
has_many :comments, :as => :commentable
|
||||
|
||||
def to_s
|
||||
msg
|
||||
end
|
||||
end
|
257
app/models/user.rb
Normal file
257
app/models/user.rb
Normal file
|
@ -0,0 +1,257 @@
|
|||
require 'country_code_select/countries'
|
||||
require 'digest/md5'
|
||||
require File.join(Rails.root, 'vendor', 'plugins', 'acts_as_versioned', 'lib', 'acts_as_versioned.rb')
|
||||
|
||||
class User < ActiveRecord::Base
|
||||
include Extra
|
||||
VERIFICATION_TIME = 604800
|
||||
|
||||
attr_protected :id, :created_at, :updated_at, :lastvisit, :lastip, :password, :version
|
||||
attr_accessor :raw_password
|
||||
|
||||
belongs_to :team
|
||||
has_one :profile, :dependent => :destroy
|
||||
has_many :bans, :dependent => :destroy
|
||||
has_many :articles, :dependent => :destroy
|
||||
has_many :movies, :dependent => :destroy
|
||||
has_many :servers, :dependent => :destroy
|
||||
has_many :votes, :dependent => :destroy
|
||||
has_many :gatherers, :dependent => :destroy
|
||||
has_many :gathers, :through => :gatherers
|
||||
has_many :groupers, :dependent => :destroy
|
||||
has_many :posts, :dependent => :destroy
|
||||
has_many :groups, :through => :groupers
|
||||
has_many :shoutmsgs, :dependent => :destroy
|
||||
has_many :issues, :foreign_key => "author_id", :dependent => :destroy
|
||||
has_many :open_issues, :class_name => "Issue", :foreign_key => "assigned_id",
|
||||
:conditions => ["issues.status = ?", Issue::STATUS_OPEN]
|
||||
has_many :posted_comments, :dependent => :destroy, :class_name => "Comment"
|
||||
has_many :comments, :class_name => "Comment", :as => :commentable, :order => "created_at ASC", :dependent => :destroy
|
||||
has_many :teamers, :dependent => :destroy
|
||||
has_many :active_teams, :through => :teamers, :source => "team",
|
||||
:conditions => ["teamers.rank >= ? AND teams.active = ?", Teamer::RANK_MEMBER, true]
|
||||
has_many :active_contesters, :through => :active_teams, :source => "contesters",
|
||||
:conditions => {"contesters.active" => true}
|
||||
has_many :active_contests, :through => :active_contesters, :source => "contest",
|
||||
:conditions => ["contests.status != ?", Contest::STATUS_CLOSED]
|
||||
has_many :past_teams, :through => :teamers, :source => "team", :group => "user_id, team_id"
|
||||
has_many :matchers, :dependent => :destroy
|
||||
has_many :matches, :through => :matchers
|
||||
has_many :predictions, :dependent => :destroy
|
||||
has_many :challenges_received, :through => :active_contesters, :source => "challenges_received"
|
||||
has_many :challenges_sent, :through => :active_contesters, :source => "challenges_sent"
|
||||
has_many :upcoming_team_matches, :through => :active_contesters, :source => "matches",
|
||||
:conditions => "match_time > UTC_TIMESTAMP()"
|
||||
has_many :upcoming_ref_matches, :class_name => "Match", :foreign_key => "referee_id",
|
||||
:conditions => "match_time > UTC_TIMESTAMP()"
|
||||
has_many :past_team_matches, :through => :active_contesters, :source => "matches",
|
||||
:conditions => "match_time < UTC_TIMESTAMP()"
|
||||
has_many :past_ref_matches, :class_name => "Match", :foreign_key => "referee_id",
|
||||
:conditions => "match_time < UTC_TIMESTAMP()"
|
||||
has_many :received_personal_messages, :class_name => "Message", :as => "recipient", :dependent => :destroy
|
||||
has_many :received_team_messages, :through => :active_teams, :source => :received_messages
|
||||
has_many :sent_personal_messages, :class_name => "Message", :as => "sender", :dependent => :destroy
|
||||
has_many :sent_team_messages, :through => :active_teams, :source => :sent_messages
|
||||
has_many :match_teams, :through => :matchers, :source => :teams, :uniq => true
|
||||
|
||||
scope :active, :conditions => {:banned => false}
|
||||
scope :with_age,
|
||||
:select => "DATE_FORMAT(FROM_DAYS(TO_DAYS(NOW())-TO_DAYS(birthdate)), '%Y')+0 AS aged, COUNT(*) as num, username",
|
||||
:group => "aged",
|
||||
:having => "num > 8 AND aged > 0"
|
||||
scope :country_stats,
|
||||
:select => "country, COUNT(*) as num",
|
||||
:conditions => "country is not null and country != '' and country != '--'",
|
||||
:group => "country",
|
||||
:having => "num > 15",
|
||||
:order => "num DESC"
|
||||
scope :posts_stats,
|
||||
:select => "users.id, username, COUNT(posts.id) as num",
|
||||
:joins => "LEFT JOIN posts ON posts.user_id = users.id",
|
||||
:group => "users.id",
|
||||
:order => "num DESC"
|
||||
scope :banned,
|
||||
:joins => "LEFT JOIN bans ON bans.user_id = users.id AND expiry > UTC_TIMESTAMP()",
|
||||
:conditions => "bans.id IS NOT NULL"
|
||||
scope :idle,
|
||||
:conditions => ["lastvisit < ?", 30.minutes.ago.utc]
|
||||
|
||||
validates_uniqueness_of :username, :email, :steamid
|
||||
validates_length_of :firstname, :in => 1..15, :allow_blank => true
|
||||
validates_length_of :lastname, :in => 1..25, :allow_blank => true
|
||||
validates_length_of :username, :in => 2..20
|
||||
validates_format_of :username, :with => /\A[A-Za-z0-9_\-\+]{2,20}\Z/
|
||||
validates_presence_of :raw_password, :on => :create
|
||||
validates_length_of :email, :maximum => 50
|
||||
validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
|
||||
validates_length_of :steamid, :maximum => 30
|
||||
validates_format_of :steamid, :with => /\A([0-9]{1,10}:){2}[0-9]{1,10}\Z/
|
||||
validates_length_of :time_zone, :maximum => 100, :allow_blank => true, :allow_nil => true
|
||||
validates_inclusion_of [:public_email], :in => [true, false], :allow_nil => true
|
||||
validate :validate_team
|
||||
|
||||
before_create :init_variables
|
||||
before_validation :update_password
|
||||
|
||||
accepts_nested_attributes_for :profile
|
||||
|
||||
acts_as_versioned
|
||||
non_versioned_columns << 'firstname'
|
||||
non_versioned_columns << 'lastname'
|
||||
non_versioned_columns << 'email'
|
||||
non_versioned_columns << 'password'
|
||||
non_versioned_columns << 'team_id'
|
||||
non_versioned_columns << 'lastvisit'
|
||||
non_versioned_columns << 'team_id'
|
||||
non_versioned_columns << 'country'
|
||||
non_versioned_columns << 'birthdate'
|
||||
non_versioned_columns << 'time_zone'
|
||||
non_versioned_columns << 'public_email'
|
||||
non_versioned_columns << 'created_at'
|
||||
|
||||
def to_s
|
||||
username
|
||||
end
|
||||
|
||||
def email_s
|
||||
email.gsub /@/, " (at) "
|
||||
end
|
||||
|
||||
def country_s
|
||||
CountryCodeSelect::Countries::COUNTRIES.each do |c|
|
||||
if c[1] == country
|
||||
return c[0]
|
||||
end
|
||||
end
|
||||
country
|
||||
end
|
||||
|
||||
def realname
|
||||
if firstname and lastname
|
||||
"#{firstname} #{lastname}"
|
||||
elsif firstname
|
||||
firstname
|
||||
elsif lastname
|
||||
lastname
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def from
|
||||
profile.town ? "#{profile.town}, #{country_s}" : "#{country_s}"
|
||||
end
|
||||
|
||||
def age
|
||||
return 0 unless birthdate
|
||||
a = Date.today.year - birthdate.year
|
||||
a-= 1 if Date.today < birthdate + a.years
|
||||
a
|
||||
end
|
||||
|
||||
def joined
|
||||
created_at.strftime("%d %b %y")
|
||||
end
|
||||
|
||||
def banned? type = Ban::TYPE_SITE
|
||||
Ban.first :conditions => ["expiry > UTC_TIMESTAMP() AND user_id = ? AND ban_type = ?", self.id, type]
|
||||
end
|
||||
|
||||
def admin?
|
||||
groups.exists? :id => Group::ADMINS
|
||||
end
|
||||
|
||||
def ref?
|
||||
groups.exists? :id => Group::REFEREES
|
||||
end
|
||||
|
||||
def verified?
|
||||
# created_at < DateTime.now.ago(VERIFICATION_TIME)
|
||||
true
|
||||
end
|
||||
|
||||
def has_access? group
|
||||
admin? or groups.exists?(:id => group)
|
||||
end
|
||||
|
||||
def new_messages
|
||||
received_personal_messages.unread_by(self) + received_team_messages.unread_by(self)
|
||||
end
|
||||
|
||||
def received_messages
|
||||
received_personal_messages + received_team_messages
|
||||
end
|
||||
|
||||
def sent_messages
|
||||
sent_personal_messages + sent_team_messages
|
||||
end
|
||||
|
||||
def upcoming_matches
|
||||
upcoming_team_matches.ordered | upcoming_ref_matches.ordered
|
||||
end
|
||||
|
||||
def past_matches
|
||||
past_team_matches.unfinished.ordered | past_ref_matches.unfinished.ordered
|
||||
end
|
||||
|
||||
def unread_issues
|
||||
issues.unread_by(self)
|
||||
end
|
||||
|
||||
def validate_team
|
||||
if team and !active_teams.exists?({:id => team.id})
|
||||
errors.add :team
|
||||
end
|
||||
end
|
||||
|
||||
def init_variables
|
||||
self.public_email = false
|
||||
self.time_zone = "Amsterdam"
|
||||
end
|
||||
|
||||
def update_password
|
||||
self.password = Digest::MD5.hexdigest(raw_password) if raw_password and raw_password.length > 0
|
||||
end
|
||||
|
||||
def send_new_password
|
||||
newpass = Verification.random_string 10
|
||||
update_attribute :password, Digest::MD5.hexdigest(newpass)
|
||||
Notifications.password(self, newpass).deliver
|
||||
end
|
||||
|
||||
def can_play?
|
||||
(gathers.count(:conditions => ["gathers.status > ?", Gather::STATE_RUNNING]) > 0) or created_at < 2.years.ago
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
true
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and (self == cuser or cuser.admin?)
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def self.authenticate username, password
|
||||
User.first :conditions => {:username => username, :password => Digest::MD5.hexdigest(password)}
|
||||
end
|
||||
|
||||
def self.get id
|
||||
id ? find(id) : ""
|
||||
end
|
||||
|
||||
def self.historic steamid
|
||||
if u = User.find_by_sql(["SELECT * FROM user_versions WHERE steamid = ? ORDER BY updated_at", steamid]) and u.length > 0
|
||||
User.find u[0]['user_id']
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.search(search)
|
||||
search ? where('username LIKE ?', "%#{search}%") : scoped
|
||||
end
|
||||
end
|
4
app/models/view_count.rb
Normal file
4
app/models/view_count.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
class ViewCount < ActiveRecord::Base
|
||||
belongs_to :viewable, :polymorphic => true
|
||||
validates_uniqueness_of :ip_address, :scope => [ :viewable_id, :viewable_type ]
|
||||
end
|
57
app/models/vote.rb
Normal file
57
app/models/vote.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
class Vote < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at, :user_id
|
||||
|
||||
validates_uniqueness_of :user_id, :scope => :votable_id
|
||||
validates_presence_of :user_id, :votable_id, :votable_type
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :votable, :polymorphic => true
|
||||
|
||||
after_create :increase_votes
|
||||
after_destroy :decrease_votes
|
||||
|
||||
def increase_votes
|
||||
if votable_type == "Option"
|
||||
votable.poll.increment :votes
|
||||
votable.poll.save
|
||||
end
|
||||
votable.increment :votes
|
||||
votable.save
|
||||
end
|
||||
|
||||
def decrease_votes
|
||||
if votable_type == "Option"
|
||||
votable.poll.decrement :votes
|
||||
votable.poll.save
|
||||
end
|
||||
votable.decrement :votes
|
||||
votable.save
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
return false unless cuser
|
||||
if votable_type == "Option"
|
||||
if votable.poll.voted?(cuser)
|
||||
return false
|
||||
end
|
||||
elsif votable_type == "Gatherer" or votable_type == "GatherMap" or votable_type == "GatherServer"
|
||||
return false unless votable.gather.users.exists? cuser
|
||||
|
||||
case votable_type
|
||||
when "Gatherer" then
|
||||
return false if votable.gather.status != Gather::STATE_VOTING
|
||||
return false if votable.gather.gatherer_votes.count(:conditions => {:user_id => user.id}) > 1
|
||||
when "GatherMap" then
|
||||
return false if votable.gather.status == Gather::STATE_FINISHED
|
||||
return false if votable.gather.map_votes.count(:conditions => {:user_id => user.id}) > 1
|
||||
when "GatherServer" then
|
||||
return false if votable.gather.status == Gather::STATE_FINISHED
|
||||
return false if votable.gather.server_votes.first :conditions => {:user_id => user.id}
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
31
app/models/week.rb
Normal file
31
app/models/week.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
class Week < ActiveRecord::Base
|
||||
include Extra
|
||||
|
||||
attr_protected :id, :updated_at, :created_at
|
||||
|
||||
validates_presence_of :contest, :map1, :map2
|
||||
validates_length_of :name, :in => 1..30
|
||||
|
||||
scope :ordered, :order => "start_date ASC"
|
||||
|
||||
belongs_to :contest
|
||||
belongs_to :map1, :class_name => "Map"
|
||||
belongs_to :map2, :class_name => "Map"
|
||||
has_many :matches
|
||||
|
||||
def to_s
|
||||
self.name
|
||||
end
|
||||
|
||||
def can_create? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_update? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
|
||||
def can_destroy? cuser
|
||||
cuser and cuser.admin?
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue