trac/upgrades/db28.py
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2023 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at https://trac.edgewall.org/.
import os.path
from trac.attachment import Attachment
from trac.util.text import exception_to_unicode, printerr, unicode_quote
from trac.util.translation import _
def do_upgrade(env, version, cursor):
"""Move attachments from the `attachments` directory into `files`, hashing
the filenames in the process."""
old_attachments_dir = os.path.join(env.path, 'attachments')
if not os.path.exists(old_attachments_dir):
return
old_stat = os.stat(old_attachments_dir)
if not os.path.exists(env.attachments_dir):
os.makedirs(env.attachments_dir)
cursor.execute("""
SELECT type, id, filename FROM attachment ORDER BY type, id
""")
for row in cursor:
move_attachment_file(env, *row)
# Try to preserve permissions and ownerships of the attachments
# directory for $ENV/files
for dir, dirs, files in os.walk(os.path.join(env.path, 'files')):
try:
if hasattr(os, 'chmod'):
os.chmod(dir, old_stat.st_mode)
if hasattr(os, 'chflags') and hasattr(old_stat, 'st_flags'):
os.chflags(dir, old_stat.st_flags)
if hasattr(os, 'chown'):
os.chown(dir, old_stat.st_uid, old_stat.st_gid)
except OSError:
pass
# Remove empty directory hierarchy
try:
for dir, dirs, files in os.walk(old_attachments_dir, topdown=False):
os.rmdir(dir)
except OSError as e:
env.log.warning("Can't delete old attachments directory %s: %s",
old_attachments_dir, exception_to_unicode(e))
# TRANSLATOR: Wrap message to 80 columns
printerr(_("""\
The upgrade of attachments was successful, but the old attachments directory:
%(src_dir)s
couldn't be removed, possibly due to the presence of files that weren't
referenced in the database. The error was:
%(exception)s
This error can be ignored, but for keeping your environment clean you should
backup any remaining files in that directory and remove it manually.
""", src_dir=old_attachments_dir, exception=exception_to_unicode(e)))
def move_attachment_file(env, parent_realm, parent_id, filename):
old_path = os.path.join(env.path, 'attachments', parent_realm,
unicode_quote(parent_id))
if filename:
old_path = os.path.join(old_path, unicode_quote(filename))
old_path = os.path.normpath(old_path)
if os.path.isfile(old_path):
new_path = Attachment._get_path(env.attachments_dir, parent_realm,
parent_id, filename)
try:
os.renames(old_path, new_path)
except OSError:
printerr(_("Unable to move attachment from:\n\n"
" %(old_path)s\n\nto:\n\n %(new_path)s\n",
old_path=old_path, new_path=new_path))
raise
else:
env.log.warning("Can't find file for 'attachment:%s:%s:%s', ignoring",
filename, parent_realm, parent_id)