Purged git history and removed sensitive information.

This commit is contained in:
Luke Barratt 2014-03-23 00:22:25 +00:00
commit 6bcc8dc76b
862 changed files with 25312 additions and 0 deletions

130
app/models/article.rb Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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