summaryrefslogtreecommitdiffstats
path: root/app.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app.rb')
-rw-r--r--app.rb113
1 files changed, 112 insertions, 1 deletions
diff --git a/app.rb b/app.rb
index 9e9cb9d..ece8b18 100644
--- a/app.rb
+++ b/app.rb
@@ -1044,7 +1044,8 @@ end
get '/people/:slug' do
data = load_people_data
- person = (data['people'] || {}).values.find { |p| p['slug'] == params[:slug] }
+ people = data['people'] || {}
+ person = people.values.find { |p| p['slug'] == params[:slug] }
halt 404 unless person
seen = {}
@@ -1057,11 +1058,121 @@ get '/people/:slug' do
@title = person['name']
@person_name = person['name']
+ @person_slug = params[:slug]
@photos = photos
@count = photos.length
+
+ if admin?
+ @named_people = people
+ .reject { |_, p| p['slug'] == params[:slug] || p['name'].nil? || p['name'] == '__pool__' }
+ .map { |k, p| { uuid: k, name: p['name'] } }
+ .sort_by { |x| x[:name].downcase }
+ @album_dirs = ([MEDIA_ROOT] + Dir.glob("#{MEDIA_ROOT}/**/*/").sort)
+ .map { |d| d.delete_prefix(MEDIA_ROOT).delete_prefix('/') }
+ .select { |d| load_album(File.join(MEDIA_ROOT, d.empty? ? '' : d))['visible'] != false }
+ end
+
erb :person
end
+post '/admin/people/:slug/bulk_reassign' do
+ require_admin!
+ rels = Array(params['rels[]'])
+ to_uuid = params['to_uuid'].to_s.strip
+ halt 400 if rels.empty? || to_uuid.empty?
+
+ data = load_people_data
+ people = data['people'] || {}
+ src = people.find { |_, p| p['slug'] == params[:slug] }
+ halt 404 unless src
+ src_uuid, src_data = src
+ halt 404 unless people.key?(to_uuid)
+
+ to_move = (src_data['members'] || []).select { |m| rels.include?(m['rel']) }
+ src_data['members'] -= to_move
+ people[to_uuid]['members'].concat(to_move)
+ people.delete(src_uuid) if src_data['members'].empty?
+ data['people'] = people
+ atomic_write(PEOPLE_PATH, JSON.pretty_generate(data))
+
+ redirect "/people/#{params[:slug]}"
+end
+
+post '/admin/photos/move_album' do
+ require_admin!
+ rels = Array(params['rels[]'])
+ dst_rel = params['dst_rel'].to_s.strip.gsub(/\/+$/, '')
+ halt 400 if rels.empty? || dst_rel =~ /\.\./
+
+ dst_abs = dst_rel.empty? ? MEDIA_ROOT : File.expand_path(dst_rel, MEDIA_ROOT)
+ halt 400 unless dst_abs.start_with?("#{MEDIA_ROOT}/") || dst_abs == MEDIA_ROOT
+ halt 400 unless File.directory?(dst_abs)
+
+ moved = []
+ rels.each do |rel|
+ rel = rel.gsub(/\.\./, '')
+ src_abs = File.expand_path(rel, MEDIA_ROOT)
+ next unless src_abs.start_with?("#{MEDIA_ROOT}/") && File.file?(src_abs)
+ filename = File.basename(src_abs)
+ src_dir = File.dirname(src_abs)
+ dst_file = File.join(dst_abs, filename)
+ next if src_dir == dst_abs
+ FileUtils.mv(src_abs, dst_file)
+ moved << { old_rel: rel, new_rel: [dst_rel, filename].reject(&:empty?).join('/'),
+ src_dir: src_dir, filename: filename }
+ end
+
+ # Update album.json in affected source directories
+ moved.group_by { |m| m[:src_dir] }.each do |src_dir, entries|
+ aj_path = File.join(src_dir, 'album.json')
+ aj = File.exist?(aj_path) ? JSON.parse(File.read(aj_path)) : default_album
+ entries.each { |e| (aj['files'] || {}).delete(e[:filename]) }
+ atomic_write(aj_path, JSON.pretty_generate(aj))
+
+ fj_path = File.join(src_dir, 'faces.json')
+ if File.exist?(fj_path)
+ fj = JSON.parse(File.read(fj_path))
+ entries.each { |e| fj.delete(e[:filename]) }
+ atomic_write(fj_path, JSON.pretty_generate(fj))
+ end
+ end
+
+ # Update faces.json in destination
+ unless moved.empty?
+ dst_fj_path = File.join(dst_abs, 'faces.json')
+ dst_fj = File.exist?(dst_fj_path) ? JSON.parse(File.read(dst_fj_path)) : {}
+ moved.each do |m|
+ src_fj = File.join(m[:src_dir], 'faces.json')
+ if File.exist?(src_fj)
+ src_data = JSON.parse(File.read(src_fj))
+ dst_fj[m[:filename]] = src_data[m[:filename]] if src_data[m[:filename]]
+ end
+ end
+ atomic_write(dst_fj_path, JSON.pretty_generate(dst_fj))
+ end
+
+ # Update people.json rel paths
+ unless moved.empty?
+ rel_map = moved.each_with_object({}) { |m, h| h[m[:old_rel]] = m[:new_rel] }
+ pd = load_people_data
+ (pd['people'] || {}).each_value do |p|
+ (p['members'] || []).each do |m|
+ m['rel'] = rel_map[m['rel']] if rel_map[m['rel']]
+ end
+ end
+ (pd['blacklist'] || []).each do |m|
+ m['rel'] = rel_map[m['rel']] if rel_map[m['rel']]
+ end
+ pool = (pd['people'] || {})['__pool__']
+ (pool['members'] || []).each { |m| m['rel'] = rel_map[m['rel']] if rel_map[m['rel']] } if pool
+ atomic_write(PEOPLE_PATH, JSON.pretty_generate(pd))
+ end
+
+ return_to = params['return_to'].to_s
+ return_to = '/people' unless return_to.start_with?('/')
+ redirect return_to
+end
+
# ── Thumbnail generation ───────────────────────────────────────────────────────
def generate_thumb(source, dest, ext)