Modify

Opened 10 months ago

Last modified 3 weeks ago

#11188 new defect

"Copying attachments" is not compatible for Trac 1.0

Reported by: jun66j5 Owned by: k0s
Priority: normal Component: TicketMoverPlugin
Severity: normal Keywords:
Cc: rjollos Trac Release: 1.0

Description (last modified by jun66j5)

The plugin copies the attachment files of the ticket when the ticket is moved. However, it determines directly the path of the attachment file without Attachment.path.

See source:ticketmoverplugin/trunk/ticketmoverplugin/ticketmover.py@13301:88-90#L80.

After Trac 1.0, the structure of the attachments directory has been changes, t:#10313. We should use Attachment.path which is available since Trac 0.10.

Attachments (3)

ticketmoverplugin_0.1.2-GU.tar.gz (3.8 KB) - added by giursino 4 weeks ago.
ticketmoverplugin_0.1.2-GU.patch (12.1 KB) - added by giursino 4 weeks ago.
ticketmoverplugin_0.1.2-FIX.patch (4.8 KB) - added by giursino 4 weeks ago.

Download all attachments as: .zip

Change History (16)

comment:1 Changed 10 months ago by jun66j5

  • Cc rjollos added
  • Description modified (diff)

comment:2 Changed 9 months ago by rjollos

Jun, we can go ahead and make changes to this plugin since k0s has retired (at least temporarily) from Trac development. I'll keep your suggested change on my TODO list, in case you don't beat me to pushing a fix!

comment:3 Changed 3 months ago by didley@…

Hello,

We are using TicketMoverPlugin in a central focus point and therefore I would like to change it by myself but I'm not a good Phyton-programmer and I don't know the API of TRAC very well.

Is it possible to give me a glue to change it by myself? Or is it much more complicated than I thought?

I found the area within the programming. I know folders now named in a hash code and the path within the environment is files/attachments/ticket.

English is not my native language and what I try is asking for a hint. It's just a question. I don't wanna annoying anybody.

It would be nice to receiving a hint.

best regards,

didley

        # copy the attachments
        src_attachment_dir = os.path.join(self.env.path, 'attachments',
                                          'ticket', str(ticket_id))
        if os.path.exists(src_attachment_dir):
            dest_attachment_dir = os.path.join(env.path, 'attachments',
                                               'ticket')
            if not os.path.exists(dest_attachment_dir):
                os.makedirs(dest_attachment_dir)
            dest_attachment_dir = os.path.join(dest_attachment_dir,
                                               str(new_ticket.id))
            shutil.copytree(src_attachment_dir, dest_attachment_dir)

        # note the previous location on the new ticket
        new_ticket.save_changes(author, 'moved from %s'
                                        % self.env.abs_href('ticket',
                                                            ticket_id))

        # location of new ticket
        new_location = env.abs_href.ticket(new_ticket.id)

comment:4 Changed 2 months ago by anonymous

Hello didley,

I'd the same problem.
Because i had to move many tickets (and ticketmover plugin didn't worked for me with batch modify) i've pulled out the stuff into a standalone script to be executed once on the server.

You might take the attachment part and patch it into the plugin.

import os
import shutil
import string

from genshi.builder import tag
from trac.env import open_environment
from trac.perm import PermissionCache
from trac.ticket import Ticket
from trac.ticket.api import ITicketActionController
from tracsqlhelper import get_all_dict, insert_row_from_dict
from trac.attachment import Attachment



src_env = open_environment('/path/to/sourcre/environment', use_cache=True)	
print 'src_env open...'

dst_env = open_environment('/path/to/destination/environment', use_cache=True)	
print "dst_env open..."

author = 'Your.Name'

PermissionCache(dst_env, author).require('TICKET_CREATE')						  

for src_ticket_id in range(1,44):
  # get the src ticket  										     
  src_ticket = Ticket(src_env, src_ticket_id)							     
  print "\nsrc_ticket %s open..." % (src_ticket_id)


  # make a new ticket from the src ticket values
  dst_ticket	    = Ticket(dst_env)
  print "dst_ticket open..."
  
  dst_ticket.values = src_ticket.values.copy()     # copies also custom fields...
  print "ticket values copied..."

  #dst_ticket.insert(when=src_ticket.time_created)
  dst_ticket.insert()
  print 'dst_ticket %s inserted...' % (dst_ticket.id)

  ############################################################################################################ 
  # copy the changelog table...
  table = 'ticket_change'
  _id	= 'ticket'
  for row in get_all_dict(src_env,					  
  		      "SELECT * FROM %s WHERE %s = %%s" % (table, _id),    
  		      str(src_ticket_id)):					  
      row[_id] = dst_ticket.id  				  
      insert_row_from_dict(dst_env, table, row)				  
  print "changelog copied..."
  														  
  														  
  ############################################################################################################ 
  # copy the attachment table...
  # in attachemnt table check for type (=ticket) AND id(=ticketID)...					   
  table = 'attachment'
  _id	= 'id'
  filenames = []
  for row in get_all_dict(src_env,
  		      "SELECT * FROM %s WHERE type = 'ticket' AND %s = %%s" % (table , _id),
  		      str(src_ticket_id)):
      row[_id] = dst_ticket.id
      insert_row_from_dict(dst_env, table, row)
      # remember file name...
      filenames.append(row['filename'])
  print "attachments checked..."




  ############################################################################################################ 
  # copy the attachments
  for filename in filenames:										   
    src_path_file = Attachment._get_path(src_env.path , 'ticket' , str(src_ticket_id) , filename)
    dst_path_file = Attachment._get_path(dst_env.path , 'ticket' , str(dst_ticket.id) , filename)     

    dst_path , dst_file = os.path.split(dst_path_file)  						   
    
    if not os.path.exists(dst_path):
      os.makedirs(dst_path)

    shutil.copyfile(src_path_file , dst_path_file)
    print "attachments copy '%s' -> '%s'..." % (src_path_file , dst_path_file)



  ############################################################################################################ 
  # note the previous location on the new ticket
  dst_ticket.save_changes(author ,'moved to here from %s' % src_env.abs_href('ticket', src_ticket_id))         
  print "dst_ticket annotated..."


  ############################################################################################################ 
  # close old ticket and point to new one				
  src_ticket['status'] = u'closed'				       
  src_ticket['resolution'] = u'duplicate'			       
  src_ticket.save_changes(author, 'moved to %s' % dst_env.abs_href('ticket', dst_ticket.id))
  print "src_ticket closed..."

Hope this helps.

With best regards,

Klaus

comment:5 Changed 2 months ago by anonymous

Hello Klaus,

thanx for your script. I will check it out.

Best regards,

didley

comment:6 Changed 5 weeks ago by jun66j5

#11631 was closed as a duplicate.

comment:7 follow-up: Changed 4 weeks ago by giursino

Hello,

I have opened the ticket #11631 that was redirect to this ticket.

So I have modified the plugin as in the attachment (ticketmoverplugin_0.1.2-GU.tar.gz) to fix the problem. Thanks to Klaus for sharing its script.

Moreover I add some other features:

  • Checkbox with the possibility to keep the ticket status (default: moved ticket will have 'new' as ticket status)
  • Checks that ticket fields are present on destination environment
  • Correct "moved to" url that didn't work when the 'base_url' was not set (in my case I have different environment on the same base directory that inherit a global trac configuration file
  • Changed version to 1.0.2-GU

Please consider to commit my patch.

Changed 4 weeks ago by giursino

comment:8 Changed 4 weeks ago by jun66j5

Could you please post the patch as unified diff format? See trac:TracDev/SubmittingPatches for good patch.

comment:9 in reply to: ↑ 7 ; follow-ups: Changed 4 weeks ago by jun66j5

Moreover I add some other features:

Also, please create a new ticket for other features.

Changed 4 weeks ago by giursino

comment:10 in reply to: ↑ 9 Changed 4 weeks ago by jun66j5

Replying to jun66j5:

Moreover I add some other features:

Also, please create a new ticket for other features.

That means the patch should be only changes for this ticket. Otherwise, it would be difficult to consider whether to apply the patch.

Changed 4 weeks ago by giursino

comment:11 follow-up: Changed 4 weeks ago by giursino

Please consider to commit all features not only this bugfix

comment:12 in reply to: ↑ 11 Changed 3 weeks ago by rjollos

Replying to giursino:

Please consider to commit all features not only this bugfix

To expand on what Jun said, the patch in it's current state puts the burden on the committer to figure out which feature or fix each line in the patch is associated with. The committer must then parse up your patch and test each change. It's unlikely to get committed unless you break up the patch into multiple patches, each targeting a specific feature or defect.

comment:13 in reply to: ↑ 9 Changed 3 weeks ago by giursino

Replying to jun66j5:

Moreover I add some other features:

Also, please create a new ticket for other features.

I've created #11641

Add Comment

Modify Ticket

Action
as new .
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.