# counter.rb $Revision: 1.20.2.1 $ # # Access counter plugin. # # 1. Usage # counter(figure, filetype = ""): Show number of all visitors. # counter_today(figure, filetype = ""): Show number of visitors for today. # counter_today(figure, filetype = ""): Show number of visitors for yesterday. # filetype: jpg, gif, png ... . # # kiriban?: Return true when the number is a kiriban. # kiriban_today?: Return true when the number is a kiriban(for today's couner). # # counter # counter 3 # coutner 3, "jpg" # counter_today 4, "jpg" # counter_yesterday # # 2. Documents # See URLs below for more details. # http://ponx.s5.xrea.com/hiki/counter.rb.html (English) # http://ponx.s5.xrea.com/hiki/ja/counter.rb.html (Japanese) # # Copyright (c) 2002 MUTOH Masao # You can redistribute it and/or modify it under GPL2. # =begin ChangeLog 2004-10-01 MUTOH Masao * Improved process against bots. @options["bot"] is now supported. 2003-11-15 MUTOH Masao * translate documents to English. 2003-02-15 MUTOH Masao * counter.datが大きくなる不具合の修正 * version 1.6.3 2002-11-26 Junichiro Kita * remove 'cgi.cookies = nil' in TDiaryCounter::main 2002-11-19 TADA Tadashi * for squeeze.rb error more. * version 1.6.2 2002-11-13 TADA Tadashi * for squeeze.rb error. 2002-10-12 MUTOH Masao * 初めて使うときに動作しなくなっていた不具合の修正。 * 1日1回保持しているユーザ情報をクリーンアップするようにした。 * version 1.6.1 2002-08-30 MUTOH Masao * データファイルが読み込めなくなったとき、1つ前のバックアップデータ を用いて復旧するようにした(その際に、1つ前のバックアップデータは counter.dat.?.bakという名前でバックアップされる)。さらに1つ前の バックアップデータからも復旧できなかった場合は全てのカウンタ値を 0にしてエラー画面が表示されないようにした。 * version 1.6.0 2002-07-23 MUTOH Masao * バックアップファイルのファイル名のsuffixを曜日(0-6の数値)にした。 従って、1週間毎に古いファイルは上書きされるのでファイルの数は 最大7つとなる。数字が新しいものが最新というわけではないので注意。 (proposed by Junichiro KITA ) * version 1.5.1 2002-07-19 MUTOH Masao * 日々単位でデータをバックアップするようにした。 @options["counter.dairy_backup"]で指定。falseを指定しない限り バックアップする。 * Date#==メソッドでnilを渡さないように修正 * require 'pstore'追加(tDiary version 1.5.x系対応) * logのフォーマット変更(全て・今日・昨日のデータを出力) * @options["counter.deny_same_src_interval"]のデフォルト値を2時間 に変更した。 * version 1.5.0 2002-05-19 MUTOH Masao * Cookieを使うことのできない同一クライアントからの連続アクセスを、 カウントアップしないようにした。 * @options["counter.deny_same_src_interval"]追加。連続GETの間隔を指定。 デフォルトで0.1時間(6分)。 * version 1.4.0 2002-05-11 MUTOH Masao * 初期値を与えない場合は5桁としていたが、「前0をなくす」に変更した。 また、前0を無くす場合は0を指定しても良い。 * version 1.3.0 2002-05-05 MUTOH Masao * @debug = true 削除 :-> * コメント変更 * version 1.2.1 2002-05-04 MUTOH Masao * tlinkプラグインからのアクセスをカウントしてしまう不具合の修正 * @options["counter.deny_user_agents"]追加 * @options["counter.deny_remote_addrs"]追加 * @options["counter.init_num"]追加。キリ番機能との関係で、counter * メソッドの引数のinit_numはobsoleteとします。 * @options["counter.kiriban"], @options["counter.kiriban_today"]追加 * キリ番機能追加(kiriban?,kiriban_today?メソッド追加) * version 1.2.0 2002-04-27 MUTOH Masao * add_header_procを使わないようにした * @options["counter.timer"]が有効にならない不具合の修正 * @options["counter.log"]追加。trueを指定するとcounter.dat と同じディレクトリにcounter.logというファイルを作成し 1日前のアクセス数を記録するようにした * cookieの値としてバージョン番号を入れるようにした * version 1.1.0 2002-04-25 MUTOH Masao * HEADでアクセスがあった場合に再びカウントされるように なってしまっていた不具合の修正(by NT) * version 1.0.4 2002-04-24 MUTOH Masao * ツッコミを入れたときにエラーが発生する不具合の修正 * version 1.0.3 2002-04-23 MUTOH Masao * データファイルを削除後、クッキーが有効期間中の端末から アクセスした場合に@todayが0になる不具合の修正 * コメント入れたときに数字が表示されない不具合の修正 * HEADでアクセスがあった場合はカウントしないようにした (reported by NT, suggested a solution by TADA Tadashi ) * version 1.0.2 2002-04-21 MUTOH Masao * CSSで_を使っているところを-に直した(reported by NT) * TDiaryCountData#upで@allが+1されない不具合の修正 * version 1.0.1 2002-04-14 MUTOH Masao * version 1.0.0 =end def counter_allow? return false if bot? if @options if @options["counter.deny_user_agents"] agents = Regexp.new("(tlink|#{@options["counter.deny_user_agents"].uniq.join('|')})", true) return false if agents =~ @cgi.user_agent end if @options["counter.deny_remote_addrs"] agents = Regexp.new("(tlink|#{@options["counter.deny_remote_addrs"].uniq.join('|')})", true) return false if agents =~ @cgi.remote_addr end end true end if ["latest", "month", "day"].include?(@mode) and @cgi.request_method == 'GET' and counter_allow? require 'date' require 'pstore' require 'ftools' eval(< 2 * 3600 } end else @yesterday = 0 @today = 1 @newestday = now end @all += 1 end def previous_access_time(remote_addr, user_agent, interval) @users = Hash.new unless @users now = Time.now ret = @users[[remote_addr, user_agent]] @users[[remote_addr, user_agent]] = now ret end def log(day, path) return unless day open(path + "/counter.log", "a") do |io| io.print day, " : ", @all, ",", @today, ",", @yesterday, "\n" end end end TOPLEVEL_CLASS module TDiaryCounter @version = "1.6.4" def run(cache_path, cgi, options) timer = options["counter.timer"] if options timer = 12 unless timer # 12 hour @init_num = options["counter.init_num"] if options @init_num = 0 unless @init_num dir = cache_path + "/counter" path = dir + "/counter.dat" today = Date.today Dir.mkdir(dir, 0700) unless FileTest.exist?(dir) cookie = nil begin cookie = main(cache_path, cgi, options, timer, dir, path, today) rescue back = (Dir.glob(path + ".?").sort{|a,b| File.mtime(a) <=> File.mtime(b)}.reverse)[0] if back copy(back, back + ".bak") copy(back, path) else rm(path) end begin cookie = main(cache_path, cgi, options, timer, dir, path, today) rescue @cnt = TDiaryCountData.new end end cookie end def main(cache_path, cgi, options, timer, dir, path, today) cookie = nil db = PStore.new(path) db.transaction do begin @cnt = db["countdata"] rescue PStore::Error end unless @cnt @cnt = TDiaryCountData.new end changed = false if new_user?(cgi, options) @cnt.up(today, dir, cgi, (options and options["counter.log"])) cookie = CGI::Cookie.new({"name" => "tdiary_counter", "value" => @version, "expires" => Time.now + timer * 3600}) changed = true end if options["counter.kiriban"] @kiriban = options["counter.kiriban"].include?(@cnt.all + @init_num) end if ! @kiriban and options["counter.kiriban_today"] @kiriban_today = options["counter.kiriban_today"].include?(@cnt.today) end if @cnt.ignore_cookie @cnt.ignore_cookie = false changed = true end #when it is kiriban time, ignore the cookie next access time. if @kiriban or @kiriban_today @cnt.ignore_cookie = true changed = true end if changed if options["counter.daily_backup"] == nil || options["counter.daily_backup"] db_bak = PStore.new(path + "." + today.wday.to_s) db_bak.transaction do db_bak["countdata"] = db["countdata"] if db["countdata"] end end db["countdata"] = @cnt end end cookie end def rm( fn, force = true ) first = true begin File.unlink fn rescue Errno::ENOENT force or raise rescue # rescue dos? begin if first then first = false File.chmod 0777, fn retry end rescue end end end def copy(old, new) File.copy(old, new) end def new_user?(cgi, options) if ! cgi.cookies or ! cgi.cookies.keys.include?("tdiary_counter") interval = options["counter.deny_same_src_interval"] if options interval = 2 unless interval # 2 hour. previous_access_time = @cnt.previous_access_time(cgi.remote_addr, cgi.user_agent, interval) if previous_access_time ret = Time.now - previous_access_time > interval * 3600 else ret = true end else ret = @cnt.ignore_cookie end ret end def format(classtype, theme_url, cnt, figure = 0, filetype = "", init_num = 0, &proc) str = "%0#{figure}d" % (cnt + init_num) result = %Q[] depth = 0 str.scan(/./).each do |num| if block_given? result << %Q[#{num}] elsif filetype == "" result << %Q[#{num}] else result << %Q[#{num}] end depth += 1 end result << "" end def called?; @called; end def called; @called = true; end def all; @cnt.all + @init_num; end def today; @cnt.today; end def yesterday; @cnt.yesterday; end def kiriban?; @kiriban; end def kiriban_today?; @kiriban_today; end module_function :new_user?, :all, :today, :yesterday, :format, :main, :run, :copy, :rm, :kiriban?, :kiriban_today? end #init_num is deprecated. #please replace it to @options["counter.init_num"] def counter(figure = 0, filetype = "", init_num = 0, &proc) TDiaryCounter.format("", theme_url, TDiaryCounter.all, figure, filetype, init_num, &proc) end def counter_today(figure = 0, filetype = "", &proc) TDiaryCounter.format("-today", theme_url, TDiaryCounter.today, figure, filetype, 0, &proc) end def counter_yesterday(figure = 0, filetype = "", &proc) TDiaryCounter.format("-yesterday", theme_url, TDiaryCounter.yesterday, figure, filetype, 0, &proc) end def kiriban? TDiaryCounter.kiriban? end def kiriban_today? TDiaryCounter.kiriban_today? end tdiary_counter_cookie = TDiaryCounter.run(@cache_path, @cgi, @options) if tdiary_counter_cookie if defined?(add_cookie) add_cookie(tdiary_counter_cookie) else @cookie = tdiary_counter_cookie if tdiary_counter_cookie end end end