o k`Ƚ@sddlmZddlmZddlmZmZddlZddlZddlZddl Z ddl Z ddl m Z m Z mZmZmZddlmZmZddlTddlTddlmZddlTdd lmZmZdd lmZmZmZm Z m!Z!dd l"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(dd l)m*Z*dd l+m,Z,m-Z-m.Z.m/Z/m0Z0ddl1m2Z2m3Z3ddl4m5Z5m6Z6m7Z7ddl8m9Z9m:Z:m;Z;mZ>ddl?m@Z@ddlAmBZBddlCmDZDGdddeEZFGdddeEZGGdddeEZHGdddeIZJGdddeKZLGdddeIZMGd d!d!eIZNdS)")datetime) TemporaryFile)ZipFile ZIP_DEFLATEDN)AdminCommandErrorIAdminCommandProvider PrefixListconsole_datetime_format get_dir_list) BoolOption IntOption)*)IPermissionPolicy) search_to_sqlshorten_result)content_dispositioncreate_zipinfo file_or_stdget_reporter_idnormalize_filename) datetime_nowformat_datetimefrom_utimestamp to_datetime to_utimestamputc)tag)exception_to_unicodepath_to_unicode pretty_size print_tableunicode_unquote)_tag_)HTTPBadRequestIRequestHandler RequestDone)INavigationContributor add_ctxtnavadd_linkadd_stylesheet web_context add_warning)Href)IWikiSyntaxProvider) format_toc@s(eZdZdZddZddZddZdS) IAttachmentChangeListenerzExtension point interface for components that require notification when attachments are created, deleted, renamed or reparented. cCdS)z#Called when an attachment is added.N attachmentr2r21/usr/lib/python3/dist-packages/trac/attachment.pyattachment_added:z*IAttachmentChangeListener.attachment_addedcCr1)z%Called when an attachment is deleted.Nr2r3r2r2r5attachment_deleted=r7z,IAttachmentChangeListener.attachment_deletedcCr1)z#Called when an attachment is moved.Nr2)r4old_parent_realm old_parent_id old_filenamer2r2r5attachment_moved@r7z*IAttachmentChangeListener.attachment_movedN)__name__ __module__ __qualname____doc__r6r8r<r2r2r2r5r04s  r0c@s eZdZdZddZddZdS)IAttachmentManipulatorzExtension point interface for components that need to manipulate attachments. Unlike change listeners, a manipulator can reject changes being committed to the database.cCr1)zNNot currently called, but should be provided for future compatibility.Nr2)reqr4fieldsr2r2r5prepare_attachmentLr7z)IAttachmentManipulator.prepare_attachmentcCr1)aValidate an attachment after upload but before being stored in Trac environment. Must return a list of ``(field, message)`` tuples, one for each problem detected. ``field`` can be any of ``description``, ``username``, ``filename``, ``content``, or `None` to indicate an overall problem with the attachment. Therefore, a return value of ``[]`` means everything is OK.Nr2)rBr4r2r2r5validate_attachmentPr7z*IAttachmentManipulator.validate_attachmentN)r=r>r?r@rDrEr2r2r2r5rAEs rAc@seZdZdZddZdS)ILegacyAttachmentPolicyDelegateaInterface that can be used by plugins to seamlessly participate to the legacy way of checking for attachment permissions. This should no longer be necessary once it becomes easier to setup fine-grained permissions in the default permission store. cCr1)aReturn the usual `True`/`False`/`None` security policy decision appropriate for the requested action on an attachment. :param action: one of ATTACHMENT_VIEW, ATTACHMENT_CREATE, ATTACHMENT_DELETE :param username: the user string :param resource: the `~trac.resource.Resource` for the attachment. Note that when ATTACHMENT_CREATE is checked, the resource ``.id`` will be `None`. :param perm: the permission cache for that username and resource Nr2)actionusernameresourcepermr2r2r5check_attachment_permissiondr7z;ILegacyAttachmentPolicyDelegate.check_attachment_permissionN)r=r>r?r@rKr2r2r2r5rF\s rFc@s$eZdZeeeeedZdZ e e Z e e ZdZeddddZedddd Zedd d d Zd dZddZddZddZddZddZddZddZddZdd Zd!d"Zd#d$Z d%d&Z!d'd(Z"d>d*d+Z#d,d-Z$d.d/Z%d0d1Z&d2d3Z'd4d5Z(d>d6d7Z)d8d9Z*d:d;Z+dz3AttachmentModule.attachment_data..ror]NATTACHMENT_CREATE) attach_href download_href can_createrrm) rrIsumrrRr|ryr}rOrJ)rVrrrm total_sizenew_attr2r2r5attachment_datas    z AttachmentModule.attachment_datac csT|jdt|t||fD]\}}}}}}t|pd} d|||| ||fVqdS)aReturn an iterable of tuples describing changes to attachments on a particular object realm. The tuples are in the form (change, realm, id, filename, time, description, author). `change` can currently only be `created`. FIXME: no iterator z SELECT type, id, filename, time, description, author FROM attachment WHERE time > %s AND time < %s AND type = %s rcreatedN)rydb_queryrr) rVstartstoprRid_rts descriptionauthortimer2r2r5 get_historys  zAttachmentModule.get_historyc cs`||||jD]$\}}}}} } } ||d|j|} d|| vr-d| | | | f|fVq dS)zReturn an event generator suitable for ITimelineEventProvider. Events are changes to attachments on resources of the given `resource_realm.realm`. rjrr4N)rrRrrJ) rVrBresource_realmrrchangerRrrrdescrrr4r2r2r5get_timeline_events#sz$AttachmentModule.get_timeline_eventscCs|d\}}|dkr|||jS|dkr8t|j|j}t|j|j}tdtt j |j tj||dddS|dkrHt |jd||j|SdS) Nurltitlez'%(attachment)s attached to %(resource)sztrac-parent-resource)rclass_)r4rIr)r|r}r{ryrmget_resource_summaryr#remosr^basenamerkr/r)rVrfieldeventr4rnamerr2r2r5render_timeline_event/s" z&AttachmentModule.render_timeline_eventc cs|jjN}t|gd|\}}|d||jf|D]/\}}} } } ||d|j| } d|| vrJt|j| |jt|j| t || t | |fVqWddS1sVwYdS)zReturn a search result generator suitable for ISearchSource. Search results are attachments on resources of the given `resource_realm.realm` whose filename, description or author match the given terms. )rrrz| SELECT id, time, filename, description, author FROM attachment WHERE type = %s AND rjrN) ryrrrRrrJr|r}get_resource_shortnamerr) rVrBrtermsdb sql_queryrSrkrrdescrr4r2r2r5get_search_results>s(    "z#AttachmentModule.get_search_resultsccs|jVdSrYrdrr2r2r5get_resource_realmsUs z$AttachmentModule.get_resource_realmscKs|jsdS|d}d}|dvr|d|d}tt|j|jddtd}|js5|||dfi|S||||jfi|S)zReturn an URL to the attachment itself. A `format` keyword argument equal to `'raw'` will be converted to the raw-attachment prefix. Nr]r4)rawroz -attachment)versionrh)rmrTpopr!r|ryr-rk)rVrIr}kwargsr]prefix parent_hrefr2r2r5r|Xs  z!AttachmentModule.get_resource_urlNcKs|js td|jdS|dkrd|jt|j|jfS|dkr%t|j|jS|jr5td|jt|j|jdStdt|j|jd S) NzUnparented attachment %(id)srjcompactz%s (%s)summaryz!Attachment '%(id)s' in %(parent)s)rkrmzAttachments of %(parent)srl)rmr"rkr{ryrr)rVrIr]rr2r2r5get_resource_descriptionns   z)AttachmentModule.get_resource_descriptioncCs2zt|j|}tj|jWStyYdSw)NF)rryrr^existsrz)rVrIr4r2r2r5rx}s   z AttachmentModule.resource_existsc Cs||jd|jj}d|jvr|t|j||j|j d\}}}|s.t t dt d|d}|dkr?t t d|d|j krJ|krXnn t t d t |j d |||_|jd d |_t|d |_d}|jD](} | ||D]\} } d}| rt|tdt| | dqvt|td| dqvqn|st|t d|||} |jd| d<| S|jdrz+t|j|j|d} ||jjdt d|dd|js| jr| j|_| Wn t yYnw|||||t|j|jdd|jdS)Nrcancelr4zNo file uploadedzUpload failed for %(filename)srrzCan't upload empty filez Maximum attachment size: %(num)snumrrhrTFz2Attachment field %(field)s is invalid: %(message)s)rmessagezInvalid attachment: %(message)s)rz"Note: File must be selected again.replace is_replacerjATTACHMENT_DELETEzYou don't have permission to replace the attachment %(name)s. You can only replace your own attachments. Replacing other's attachments requires ATTACHMENT_DELETE permission.r)rJrIrequirermrSredirectr|ryr}getfilersr"rNrrrTrrr manipulatorsrEr,r#rstrongrrstriprqinsert)rVrBr4parent_resourcerfileobjfilesize upload_failedvalid manipulatorrrrold_attachmentr2r2r5rs               zAttachmentModule._do_savecCsP||jdt|j|jj|j}d|jvr||| ||dS)Nrr) rJrIrr|ryrmr}rSrrq)rVrBr4rr2r2r5rs   zAttachmentModule._do_deletecCs0||jddtdt|j|jd|dS)Nrrqz%(attachment)s (delete)r3moderr4)rJrIrr"r{ryrVrBr4r2r2r5rsz'AttachmentModule._render_confirm_deletecCs&||jddt|||jdS)Nrrf)rrr4rN)rJrIrrrNrr2r2r5rszAttachmentModule._render_formc s|dur |t|}tdd|D}||jkr(ttdt|jdtddddd |j t d d t |j f}d td |fdd}d}tddd`}t|dtD} |D]9} t| j| j| jd} z| } | | | Wdn1swYWn tyYqgw|||}qgWdn1swY|||Wdt1swYt)NcsrrYrrr2r2r5rrz4AttachmentModule._download_as_zip..z&Maximum total attachment size: %(num)srzDownload failedz Content-Typezapplication/zipzattachments-%s-%s.zipz[/\\:]-Content-Dispositioninlinecsb|}||d||}|dkr)|t|d}||t|8}|dks||d|S)NrrM)tellseekreadminwritelen)rrend remainingchunkrBr2r5 write_partials    z8AttachmentModule._download_as_zip..write_partialrztrac-z.zip)rsuffixw)mtimecomment)rr+rrOrsr"r send_response send_headerrRr_substrrkr end_headersrrrrrdateropenwritestrrrzr&) rVrBrmrrrrposrzipfiler4zipinfofdr2rr5r~sV         z!AttachmentModule._download_as_zipcCs dd|t||d}d|fS)Nlist)rr4rrr)rr+)rVrBrmrr2r2r5rs zAttachmentModule._render_listc Cs||jdd||jv}||jt|dt|j|j|d}|}t |j}| d}| d| |j |}|jd} | dkrU|||jj|gn/| d vr|jsb|d d | d krid }n|smd}d|vr}|||} |d| }||j||jr|r|d st|j|j|jd d} t|d| td|t|j|j|jdd} t|d| td||jd|j ||jt||j|t !|"j#||j | dgd|d<|WdS1swYdS)Nrrrerirr]ro)rtxtrr4rz text/plainzapplication/octet-streamzcharset=z ; charset=r alternatez Plain TextrzOriginal Formatz.Rendering preview of file %s with mime-type %slineno) annotationspreview)$rJrIrcheck_modifiedrrr{ryrMimeviewrr get_mimetyperrSrTr~rmrPr get_charset send_filer^ startswithr|r}r)r"logdebug preview_datar+rfstatfilenost_size) rVrBr4 can_deleterr mimeviewstr_data mime_typer]charsetplaintext_hrefraw_hrefr2r2r5rsp              $zAttachmentModule._render_viewcCsl||\}}}|dd}d} t|dkrKt|j} |d| vr4t|d|d|j|d} n|d| vrJt|d|d|j|d} n|j |j|} | rd| | vrzt |j| Wn t ymYnBwt |j| |jdd} t|j| } |drtj|d | || d St |j| |j} ttj|d | | |d tjd d | |tdd Stj|ddS)N:rrirrrrr4)rr}r)rrr}u​z trac-rawlinkDownloadzmissing attachment)r) split_linksplitrResourceSystemryget_known_realmsrurrRrIrJrrzr|r}r{rrar")rV formatternstargetlabellinkparamsfragmentidsr4 known_realmsr&rr}r2r2r5rVsL         zAttachmentModule._format_linkrY)-r=r>r? implementsr%r'r.IResourceManagerrRis_valid_default_handlerExtensionPointr0change_listenersrAr CHUNK_SIZEr rNrOr rPrWrZrcrrrrrrrrrrr|rrxrrrrr~rrrr2r2r2r5rLtsV =   D  - >rLc@seZdZdZejZeddZ d+ddZddZ d d Z d d Z e d dZ edZe ddZeddZeddZddZd,ddZd-ddZe ddZe ddZe d d!Zd"d#Z  $d.d%d&Zd'd(Zd)d*ZdS)/rz+Represents an attachment (new or existing).cCst|j|j|j|jSrY)rurrrrRrrr2r2r5rIs  zAttachment.resourceNcCst|tr|}|jj|_t|jj|_|j|_n ||_t||_||_||_ |jr1| |jdSd|_d|_ d|_ d|_ d|_dSrY) isinstancerurmrRrrrkrrry_fetchrrrr)rVry#parent_realm_or_attachment_resourcerrrIr2r2r5__init__s     zAttachment.__init__cCsd|jj|jfS)Nz<%s %r>) __class__r=rrr2r2r5__repr__szAttachment.__repr__cCs6||_||_|r t|nd|_t|pd|_||_dS)Nr)rrintrrrr)rVrrrrrr2r2r5_from_databases  zAttachment._from_databasecCsN|jd|jt|j|fD] }|j|dS||_ttd|j dtd)Nz SELECT filename, description, size, time, author FROM attachment WHERE type=%s AND id=%s AND filename=%s ORDER BY time z&Attachment '%(title)s' does not exist.rzInvalid Attachment) ryrrrrrErrzr"r)rVrrowr2r2r5r?s zAttachment._fetchcCs^tj||}t|d}tj||dd|}|r)tj|||}tj|S)zGet the path of an attachment. WARNING: This method is used by db28.py for moving attachments from the old "attachments" directory to the "files" directory. Please check all changes so that they don't break the upgrade. utf-8rr) rr^joinhashlibsha1encode hexdigest_get_hashed_filenamenormpath)clsattachments_dirrrrr^hashr2r2r5 _get_paths  zAttachment._get_pathz\.[A-Za-z0-9]+\ZcCs6t|d}|j|}|r||dS|S)aGet the hashed filename corresponding to the given filename. WARNING: This method is used by db28.py for moving attachments from the old "attachments" directory to the "files" directory. Please check all changes so that they don't break the upgrade. rHr)rJrKrLrM _extension_researchgroup)rPrrRr`r2r2r5rNs zAttachment._get_hashed_filenamecCs||jj|j|j|jSrY)rSryrQrrrrr2r2r5r^szAttachment.pathcCsd|j|j|jfS)N %s:%s: %s)rrrrr2r2r5rszAttachment.titlec Cs|jsJd|jjC}|d|j|j|jf|j}tj|rGzt|Wnt yF}z|jj d|t |ddt tdd}~wwWdn1sQwY|jj d|jt|jjD]}||qedS) z\Delete the attachment, both the record in the database and the file itself. z%Cannot delete non-existent attachmentz` DELETE FROM attachment WHERE type=%s AND id=%s AND filename=%s z'Failed to delete attachment file %s: %sT tracebackzCould not delete attachmentNzAttachment removed: %s)rrydb_transactionrrr^risfileunlinkOSErrorrerrorrrsr"inforrLr<r8)rVrr^elistenerr2r2r5rqs.      zAttachment.deletecCs|j|||dddS)zMove the attachment, changing one or more of its parent realm, parent id and filename. The new parent resource must exist. :since: 1.3.2 T)new_parent_must_existN)_move)rV new_realmnew_id new_filenamer2r2r5moveszAttachment.movec Cs|rt|nd|_d|_|durtt}n t|tst|t}||_t |j |j }t |j |s;ttdt|j |d||jsMttd||j |j d|j}t|tjs\t||||\}}|C|j j/}|d|j |j ||jt||j|jft||||_|j j d|j!|jWdn1swYWdn1swYt"|j j#D]} | $|qdS) zBCreate a new Attachment record and save the file content. rNz1%(parent)s doesn't exist, can't create attachmentrlzACannot create attachment "%(att)s" as %(realm)s:%(id)s is invalid)attrRrkz4INSERT INTO attachment VALUES (%s,%s,%s,%s,%s,%s,%s)zNew attachment: %s by %s)%rDrrrrr>rrrrurrrxryrzr"r{_is_valid_pathr^rsraccessF_OKmakedirs_create_unique_filerZrrrshutil copyfileobjrr_rrLr<r6) rVrrrtrdir targetfilerrar2r2r5r sR          zAttachment.insertccs<|d|t|fD]}t|||}|j||Vq dS)zIterator yielding all `Attachment` instances attached to resource identified by `parent_realm` and `parent_id`. :return: a tuple containing the `filename`, `description`, `size`, `time` and `author`. z SELECT filename, description, size, time, author FROM attachment WHERE type=%s AND id=%s ORDER BY time N)rrrrE)rPryrrrGr4r2r2r5r3s   zAttachment.selectc Csd}|j||||D] }tj|j}|q Wdn1s%wY|rTzt|WdStyS}z|j d|t |ddWYd}~dSd}~wwdS)z4Delete all attachments of a given resource. N(Can't delete attachment directory %s: %sTrX) rZrrr^dirnamerqrmdirr]rr^r)rPryrrattachment_dirr4r`r2r2r5 delete_allCs$  zAttachment.delete_allc Csd}|j!t||||D]}tj|j}|||qWdn1s)wY|rXzt|WdStyW}z|j d|t |ddWYd}~dSd}~wwdS)zJReparent all attachments of a given resource to another resource. NrsTrX) rZrrrr^rtrcrur]rr^r) rPryrrrdrervr4r`r2r2r5 reparent_allTs$ zAttachment.reparent_allcCsH|j}|jjd|zt|d}W|Sty#ttd|jdw)NzTrying to open attachment at %srbz#Attachment '%(filename)s' not foundr) r^ryrrrIOErrorrzr"r)rVr^r r2r2r5res  zAttachment.openFc Cs|js ttd|dur|j}|dur|jnt|}|dur"|j}|||f|j|j|jfkr5ttd||jj|||}d|||f}|r`t ||}t |j|s_t tdt |j|dn|t |jvrqt td|d||sttd|j|dtj|rttd |j|d|jj[}|d ||||j|j|jftj|} tj| st| |j} tj| rzt| |Wn"ty} z|jjd | t| d d ttd|jdd} ~ wwWdn1swY|j} |j} |j}||_||_||_|jjd|j| |kp| |k}t|jjD]!}t |dr1|!|| | ||rAt |drA|"|| | q!dS)z~Move the attachment, changing one or more of its parent realm, parent id and filename. :since: 1.3.2 z%Cannot rename non-existent attachmentNzAttachment not modifiedrWz/%(target)s doesn't exist, can't move attachment)r1z:Cannot move attachment "%(att)s" as "%(title)s" is invalid)rhrzDCannot move attachment "%(att)s" to "%(title)s" as it already existsz~UPDATE attachment SET type=%s, id=%s, filename=%s WHERE type=%s AND id=%s AND filename=%s z%Failed to move attachment file %s: %sTrXz%Could not move attachment "%(title)s"rFzAttachment moved: %sr<attachment_reparented)#rrsr"rrrrSryrQrurxrzr{r,r-rirr^rrZrtrlr[renamer]rr^rrr_rLr<hasattrr<r{)rVrdrerfrbnew_path new_titlenew_parent_resourcerrtr^r` old_realmold_idr; reparentedrar2r2r5rcos                zAttachment._movec Cstj|}tjtjtj}ttdr|tj7}d} tj|| |}z|t t ||ddfWSt yf}z&|j t jkrC|d7}|dkrQtd|d|d||df}WYd}~nd}~wwq) NO_BINARYriiwbdzFailed to create unique name: z%s.%d%sr)rr^splitextO_CREATO_WRONLYO_EXCLr}rrIrNfdopenrr]errnoEEXIST Exception)rVrqrpartsflagsidxr^r`r2r2r5rms&     "zAttachment._create_unique_filecCs tj|jj|g}||jjkS)zkReturn True if the path to the attachment is inside the environment attachments directory. )rr^ commonprefixryrQ)rVr^rr2r2r5ris zAttachment._is_valid_path)NN)NNNrY)NNNF)r=r>r?r@rLrRpropertyrIrArCrEr? classmethodrSr_compilerTrNr^rrqrgrrrwrxrrcrmrir2r2r2r5rsD         )     M rc@sReZdZdZeeeeZe j Z ddddddddd d d dd Z d dZ dS)LegacyAttachmentPolicyzuDefault permission policy for the attachment system. Authenticated users can delete attachments they added. TICKET_APPEND WIKI_MODIFYMILESTONE_MODIFY)ticketwiki milestone TICKET_VIEW WIKI_VIEWMILESTONE_VIEW TICKET_ADMIN WIKI_DELETEMILESTONE_DELETE)rrrc Cs|j|}|r|r|j|jkrdS|jr*|dkr*|dkr*t|j|}||jkr*dS||jj}|rG|||jv}|sE|j d||||S|j D]} | ||||}|dur\|SqJdS)N anonymousrTzr?r@r8rr;rFrrLrRrrr2r2r2r5rs  rc@seZdZdZeeddZddZddZdd Z d d Z d d Z ddZ ddZ ddZddZd!ddZddZddZd"dd ZdS)#AttachmentAdminz:trac-admin command provider for attachment administration.ccsjddd|j|jfVddd|j|jfVddd |j|jfVd d d |j|jfVd dd|j|j fVdS)Nzattachment listz zfList attachments of a resource The resource is identified by its realm and identifier.zattachment addz( [author] [description]zAttach a file to a resource The resource is identified by its realm and identifier. The attachment will be named according to the base name of the file. zattachment removez zlRemove an attachment from a resource The resource is identified by its realm and identifier.zattachment movez/ zRename or move an attachment to another resource The resource is identified by its realm and identifier. zattachment exportz [destination]zExport an attachment from a resource to file or stdout The resource is identified by its realm and identifier. If no destination is specified, the attachment is output to stdout. ) _complete_list_do_list _complete_add_do_add_complete_remove _do_remove_complete_move_do_move_complete_export _do_exportrr2r2r5get_admin_commandss, z"AttachmentAdmin.get_admin_commandscCs t|j}tdd|DS)NcSsg|] }|dkr|dqS)r4r'r2)reachr2r2r5 4sz2AttachmentAdmin.get_realm_list..)r,ryrr-)rVrsr2r2r5get_realm_list2s zAttachmentAdmin.get_realm_listcCs,|dd}t|dkrttd|d|S)Nr'rir(z$Invalid resource identifier '%(id)s'rj)r+rrr")rVrIresultr2r2r5split_resource7s  zAttachmentAdmin.split_resourcecCs(||\}}ddt|j||DS)NcSsg|]}|jqSr2rrr.r2r2r5r@sz7AttachmentAdmin.get_attachment_list..)rrrryrVrIrRrr2r2r5get_attachment_list>sz#AttachmentAdmin.get_attachment_listcCst|dkr |SdS)Nri)rrrVrSr2r2r5rBs zAttachmentAdmin._complete_listcCs0t|dkr |St|dkrt|dSdS)Nrir()rrr rr2r2r5rFs   zAttachmentAdmin._complete_addcCs2t|dkr |St|dkr||dSdS)Nrir(rrrrrr2r2r5rL  z AttachmentAdmin._complete_removecCs2t|dvr |St|dvr||dSdS)N)rir)r(rrrr2r2r5rRrzAttachmentAdmin._complete_movecCs2t|dkr ||St|dkrt|dSdS)Nrr()rrr rr2r2r5rXs    z AttachmentAdmin._complete_exportcCsP||\}}tddt|j||DtdtdtdtdtdgdS)NcSs.g|]}|jt|j|jt|jt|jfqSr2)rrrrrrr rrr2r2r5r`s  z,AttachmentAdmin._do_list..NameSizeAuthorDate Description)rr rrryr"rr2r2r5r^szAttachmentAdmin._do_listtracrhc Cs~||\}}t|j||}||_||_ttj|}t |d} | || tj |WddS1s8wYdS)Nry) rrryrrrrr^rrrgetsize) rVrIr^rrrRrr4rfr2r2r5rgs "zAttachmentAdmin._do_addcCs*||\}}t|j|||}|dSrY)rrryrq)rVrIrrRrr4r2r2r5rps zAttachmentAdmin._do_removec Cs>||\}}||\}}t|j|||} | |||dSrY)rrryrg) rV old_resourceold_name new_resourcenew_namerrrdrer4r2r2r5ruszAttachmentAdmin._do_moveNc Cs||\}}t|j|||}|dur0tj|r tj||}tj|r0tt dt |d| ,}t |d}t ||Wdn1sKwYWddSWddS1scwYdS)NzFile '%(name)s' existsrr)rrryrr^isdirrIr[rr"rrrrnro) rVrIr destinationrRrr4inputoutputr2r2r5r{s     "zAttachmentAdmin._do_export)rrhrY)r=r>r?r@r8rrrrrrrrrrrrrrrr2r2r2r5r s""  r)Ortempfilerr rrrrJrr_rn trac.adminrrrr r trac.configr r trac.core trac.mimeview trac.permr trac.resource trac.searchrr trac.utilrrrrrtrac.util.datefmtrrrrrrtrac.util.htmlrtrac.util.textrrrr r!trac.util.translationr"r#trac.webr$r%r&trac.web.chromer'r(r)r*r+r, trac.web.hrefr- trac.wiki.apir.trac.wiki.formatterr/ Interfacer0rArF ComponentrLobjectrrrr2r2r2r5sL         T2