blob: cbafac044cdbc677a1d38dc91a3b1c49cb93ec7a [file] [log] [blame]
require 'json'
# Access to documents/* (except member_apps)
module ASF
module DocumentUtils # This module is also used for member_apps
MAX_AGE = 600 # 5 minutes
# N.B. must check :cache config each time to allow for test overrides
# check cache age and get settings
def self.check_cache(type, cache_dir: ASF::Config.get(:cache), warn: true)
file, _ = ASF::SVN.listingNames(type, cache_dir)
mtime = begin
File.mtime(file)
rescue Errno::ENOENT
0
end
age = (Time.now - mtime).to_i
stale = age > MAX_AGE
if warn && stale
Wunderbar.warn "Cache for #{type} is older than #{MAX_AGE} seconds"
# Wunderbar.warn caller(0, 10).join("\n")
end
return [cache_dir, stale, file, age]
end
# N.B. must check :cache config each time to allow for test overrides
# create/update cache file
def self.update_cache(type, env, cache_dir: ASF::Config.get(:cache))
_cache_dir, stale, file, age = check_cache(type, cache_dir: cache_dir, warn: false)
if stale
require 'whimsy/asf/rack'
ASF::Auth.decode(env)
# TODO: Downdate to info
Wunderbar.warn "Updating listing #{file} #{age} as #{env.user}"
filerev, svnrev = ASF::SVN.updatelisting(type, env.user, env.password, false, cache_dir)
if filerev && svnrev # it worked
FileUtils.touch file # last time it was checked
else
# raise IOError.new("Failed to fetch iclas.txt: #{svnrev}")
Wunderbar.warn("User #{env.user}: failed to update #{type}: #{svnrev}")
end
end
end
end
# Common class for access to documents/cclas/
class CCLAFiles
STEM = 'cclas'
def self.update_cache(env)
ASF::DocumentUtils.update_cache(STEM, env)
end
# listing of top-level icla file/directory names
# Directories are listed without trailing "/"
def self.listnames
cache_dir = ASF::DocumentUtils.check_cache(STEM).first
_, list = ASF::SVN.getlisting(STEM, nil, true, false, cache_dir)
list
end
# Does an entry exist?
def self.exist?(name)
self.listnames.include?(name)
end
end
# Common class for access to documents/cclas/
class GrantFiles
STEM = 'grants'
def self.update_cache(env)
ASF::DocumentUtils.update_cache(STEM, env)
end
# listing of top-level grants file/directory names
# Directories are listed without trailing "/"
def self.listnames
cache_dir = ASF::DocumentUtils.check_cache(STEM).first
_, list = ASF::SVN.getlisting(STEM, nil, true, false, cache_dir)
list
end
# Does an entry exist?
def self.exist?(name)
self.listnames.include?(name)
end
end
# Common class for access to documents/iclas/ directory
# Only intended for use by secretary team
class ICLAFiles
@@tag = nil # probably worth caching iclas
@@list = nil # this list includes trailing '/' so can detect directories correctly
# Turns out that select{|l| ! l.end_with?('/') && l.start_with?("#{stem}.")} is quite slow,
# so create hashes from the list
@@h_claRef = nil # for matching claRefs
@@h_stem = nil # for matching stems
STEM = 'iclas'
def self.update_cache(env)
ASF::DocumentUtils.update_cache(STEM, env)
end
# search icla files to find match with claRef
# matches if the input matches the full name of a file or directory or
# it matches with an extension
# Returns the basename or nil if no match
def self.match_claRef(claRef)
unless @@h_claRef
h_claRef = {}
listnames.map do |l|
# Match either full name (e.g. directory) or stem (e.g. name.pdf)
if l.end_with? '/'
h_claRef[l.chomp('/')] = l.chomp('/')
elsif l.include?('.')
h_claRef[l.split('.')[0]] = l
else
h_claRef[l] = l
end
end
@@h_claRef = h_claRef
end
@@h_claRef[claRef]
end
# is the name a directory?
def self.Dir?(name)
listnames.include? name + '/'
end
# return a list of names matching stem.*
# Does not return directories
def self.matchStem(stem)
unless @@h_stem
h_stem = Hash.new {|h, k| h[k] = []}
listnames.map do |l|
h_stem[l.split('.')[0]] << l unless l.end_with?('/')
end
@@h_stem = h_stem
end
@@h_stem[stem]
end
# This returns the list of names in the top-level directory
# directory names are terminated by '/'
def self.listnames
# iclas.txt no longer updated by cronjob
cache_dir = ASF::DocumentUtils.check_cache(STEM).first
@@tag, list = ASF::SVN.getlisting(STEM, @@tag, false, false, cache_dir)
if list # we have a new list
# update the list cache
@@list = list
# clear the hash caches
@@h_claRef = nil
@@h_stem = nil
end
@@list
end
end
class EmeritusFiles
@base = 'emeritus'
def self.listnames(getDates=false)
_, list = ASF::SVN.getlisting(@base, nil, true, getDates)
list
end
# Find the file name that matches a person
# return nil if not exactly one match
# TODO: should it raise an error on multiple matches?
def self.find(person, getDate=false)
# TODO use common stem name method
name = (person.attrs['cn'].first rescue person.member_name).force_encoding('utf-8').
downcase.gsub(' ', '-').gsub(/[^a-z0-9-]+/, '') rescue nil
id = person.id
files = self.listnames(getDate).find_all do |file|
if file.is_a?(Array) # we have [epoch, file]
file = file[1]
end
stem = file.split('.')[0] # directories don't have a trailing /
stem == id or stem == name
end
# Only valid if we match a single file or directory
if files.length == 1
files.first
else
nil
end
end
# return the svn path to an arbitrary file
def self.svnpath!(file)
ASF::SVN.svnpath!(@base, file)
end
# Find the svnpath to the file for a person
# Returns
# svnpath, filename
# or
# nil, nil if not found
def self.findpath(person)
path = nil
file = find(person)
path = svnpath!(file) if file
[path, file]
end
# Extract the file name from an svn url
# param rooturl the svn url of the directory
# param fileurl the svn url of the complete file
# return the file name or nil if the file is not in the directory
def self.extractfilenamefrom(rooturl, fileurl)
return nil unless fileurl
# does the root match the file url?
index = fileurl.index(rooturl)
return nil unless index.zero?
# root matches, return file name (end of fileurl)
fileurl[rooturl.length..-1]
end
# Extract the file name if it is in emeritus directory
# nil if it is not in this directory
def self.extractfilename(fileurl)
return nil unless fileurl
root_url = ASF::SVN.svnurl(@base) + '/'
extractfilenamefrom(root_url, fileurl)
end
end
class EmeritusRequestFiles < EmeritusFiles
@base = 'emeritus-requests-received'
end
class EmeritusRescindedFiles < EmeritusFiles
@base = 'emeritus-requests-rescinded'
end
class EmeritusReinstatedFiles < EmeritusFiles
@base = 'emeritus-reinstated'
end
class COIFiles < EmeritusFiles
@base = 'conflict-of-interest'
end
end