edgewall/trac

View on GitHub
trac/upgrades/db28.py

Summary

Maintainability
A
1 hr
Test Coverage
# -*- 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)