
    p:i4                     "   d dl Z d dlZd dlmZmZ d dlmZmZ d dlm	Z	 d dl
mZ d dlmZ d dlmZ d dlmZ d d	lmZ d d
lmZmZ d dlmZ d dlmZ d dlmZmZ d dlmZm Z  d dl!m"Z" d dl#m$Z$ d dl%m&Z&  G d de          Z' G d de&          Z(dS )    N)
BoolOptionOption)	Component
implements)NotificationSystem)PermissionCache)Resource)Ticket)TicketChangeEvent)as_int)datetime_nowutc)tag)exception_to_unicode)_	cleandoc_)IRepositoryChangeListenerRepositoryManager)ChangesetModule)format_to_html)WikiMacroBasec                   V   e Zd ZdZ ee            edddd          Z edddd          Z edd	d
d          Z	 e
dddd          Z e
dddd          ZdZedz   ZdededZed             Z ej        edz             ZdZd Zd Zd Zd Zd Zd Zd Zd  Zd! Zd" Zd# Z dS )$CommitTicketUpdatera  Update tickets based on commit messages.

    This component hooks into changeset notifications and searches commit
    messages for text in the form of:
    {{{
    command #1
    command #1, #2
    command #1 & #2
    command #1 and #2
    }}}

    Instead of the short-hand syntax "#1", "ticket:1" can be used as well,
    e.g.:
    {{{
    command ticket:1
    command ticket:1, ticket:2
    command ticket:1 & ticket:2
    command ticket:1 and ticket:2
    }}}

    Using the long-form syntax allows a comment to be included in the
    reference, e.g.:
    {{{
    command ticket:1#comment:1
    command ticket:1#comment:description
    }}}

    In addition, the ':' character can be omitted and issue or bug can be used
    instead of ticket.

    You can have more than one command in a message. The following commands
    are supported. There is more than one spelling for each command, to make
    this as user-friendly as possible.

      close, closed, closes, fix, fixed, fixes::
        The specified tickets are closed, and the commit message is added to
        them as a comment.

      references, refs, addresses, re, see::
        The specified tickets are left in their current status, and the commit
        message is added to them as a comment.

    A fairly complicated example of what you can do is with a commit message
    of:

        Changed blah and foo to do this or that. Fixes #10 and #12,
        and refs #12.

    This will close #10 and #12, and add a note to #12.
    ticketcommit_ticket_update_envelope zRequire commands to be enclosed in an envelope.

        Must be empty or contain two characters. For example, if set to `[]`,
        then commands must be in the form of `[closes #4]`.z#commit_ticket_update_commands.closez#close closed closes fix fixed fixesz7Commands that close tickets, as a space-separated list.z"commit_ticket_update_commands.refsz addresses re references refs seezCommands that add a reference, as a space-separated list.

        If set to the special value `<ALL>`, all tickets referenced by the
        message will get a reference to the changeset. commit_ticket_update_check_permstruezCheck that the committer has permission to perform the requested
        operations on the referenced tickets.

        This requires that the user names be the same for Trac and repository
        operations.commit_ticket_update_notifyz7Send ticket change notification when updating a ticket.z(?:#|(?:ticket|issue|bug)[: ]?)z([0-9]+(?:#comment:([0-9]+|description))?z((?P<action>[A-Za-z]*)\s*.?\s*(?P<ticket>z(?:(?:[, &]*|[ ]?and[ ]?)z)*)c                     t          j        | j        dd                   t          j        | j        dd                   }}t          j        || j        z   |z             S )Nr         )reescapeenvelopecompileticket_command)selfbeginends      U/var/www/html/trac/venv/lib/python3.11/site-packages/tracopt/ticket/commit_updater.py
command_rezCommitTicketUpdater.command_re   sV    iac 233iac 233 z%$"55;<<<    z([0-9]+)Nc                     |                      |          rd S |                     |j                  }|                     ||          }|                     |||t          t                               d S N)_is_duplicate_parse_messagemessagemake_ticket_comment_update_ticketsr   r   )r(   repos	changesetticketscomments        r+   changeset_addedz#CommitTicketUpdater.changeset_added   sp    i(( 	F%%i&788**5)<<Wi,s:K:KLLLLLr-   c                    |                      |          rd S |                     |j                  }i ||                     |j                  t          fd|                                D                       }|                     ||          }|                     |||t          t                               d S )Nc              3   0   K   | ]}|d          v|V  dS )r   N ).0eachold_ticketss     r+   	<genexpr>z9CommitTicketUpdater.changeset_modified.<locals>.<genexpr>   s=       6 6q'44 44446 6r-   )	r0   r1   r2   dictitemsr3   r4   r   r   )r(   r5   r6   old_changesetr7   r8   r?   s         @r+   changeset_modifiedz&CommitTicketUpdater.changeset_modified   s    i(( 	F%%i&788$--m.CDDK 6 6 6 6 6 6 6 6 6**5)<<Wi,s:K:KLLLLLr-   c                 b    |j         |j        |j        |j        f}|| j        k    r	|| _        dS dS )NFT)revr2   authordate_last_cset_id)r(   r6   cset_ids      r+   r0   z!CommitTicketUpdater._is_duplicate   s=    =)"3Y5E>#d(((!(D5tr-   c                    | j                             |          }|                                 }i }|D ]}|                    dd          \  }}|                    |                                          }|s$| j                                        dk    r| j        }|rS| j	        
                    |          D ]8}	|                    t          |	          g                               |           9|S )z:Parse the commit message and return the ticket references.actionr   z<ALL>)r,   finditer_get_functionsgroupgetlowercommands_refsstripcmd_refs	ticket_refindall
setdefaultintappend)
r(   r2   
cmd_groups	functionsr7   mcmdtktsfunctkt_ids
             r+   r1   z"CommitTicketUpdater._parse_message   s    _--g66
''))	 	E 	EA(33IC==--D %D.4466'AA} E"n44T:: E EF&&s6{{B77>>tDDDDr-   c                 $   |j         }t          |          }t          |                    |                    }|j        r|d|j        z   z  }|d|j        z   z  }t	          j        d          |||j        ||j                                        fz  S )z2Create the ticket comment from the changeset data./z            In [changeset:"%s" %s]:
            {{{#!CommitTicketReference repository="%s" revision="%s"
            %s
            }}})rF   strdisplay_revreponametextwrapdedentr2   rS   )r(   r5   r6   rF   	revstringdrevs         r+   r3   z'CommitTicketUpdater.make_ticket_comment   s    mHH	5$$S))**> 	)u~--IC%.((D     !$ (..002	2 	2r-   c           	      x   |                      |          }t          | j        |          }|                                D ]\  }}| j                            d|           d}		 | j        j        5  t          | j        |          }
 ||
j                  }|D ]} ||
||          durd}	|	r|
	                    |||           ddd           n# 1 swxY w Y   |	r| 
                    |
||j        |           # t          $ r3}| j                            d|t          |                     Y d}~d}~ww xY wdS )z*Update the tickets with the given comment.zUpdating ticket #%dFTNz0Unexpected error while processing ticket #%s: %s)	_authnamer   envrB   logdebugdb_transactionr
   resourcesave_changes_notifyrG   	Exceptionerrorr   )r(   r7   r6   r8   rH   authnamepermr`   cmdssaver   ticket_permr]   es                 r+   r4   z#CommitTicketUpdater._update_tickets   s   >>),,tx22#MMOO 	K 	KLFDHNN0&999DKX, E E#DHf55F"&$v"7"7K# ( (3vy+>>eKK#'D E++HgtDDDE E E E E E E E E E E E E E E  JLLy/?III K K K  )*02Fq2I2IK K K K K K K KK	K 	Ks=   "C:.ACC:C	C:C	"C::
D7)D22D7c                    | j         sdS t          d||||          }	 t          | j                                       |           dS # t          $ r9}| j                            d|j        t          |                     Y d}~dS d}~ww xY w)z"Send a ticket update notification.Nchangedz8Failure sending notification on change to ticket #%s: %s)	notifyr   r   rl   rs   rm   rt   idr   )r(   r   rH   rG   r8   eventrz   s          r+   rr   zCommitTicketUpdater._notify   s    { 	F!)VT67KK	4tx((//66666 	4 	4 	4HNN ,-3Y/224 4 4 4 4 4 4 4 4	4s   'A 
B
.BB
c                     i }t          |           D ]^}|                    d          st          | |          }t          | d|dd         z   d                                          D ]}|||<   _|S )z4Create a mapping from commands to command functions.cmd_	commands_   Nr   )dir
startswithgetattrsplit)r(   r[   r>   r_   r]   s        r+   rN   z"CommitTicketUpdater._get_functions   s    	II 	& 	&D??6** 4&&Dt[48%;R@@FFHH & &!%	#&r-   c                     | j         j                            dd          r|j                                        n|j        S )ziReturns the author of the changeset, normalizing the casing if
        [trac] ignore_author_case is true.tracignore_auth_case)rl   configgetboolrG   rQ   )r(   r6   s     r+   rk   zCommitTicketUpdater._authname  s@     (/))&2DEE%y%%'''$	%r-   c                     |                      |          }| j        r'd|vr#| j                            d||j                   dS d|d<   d|d<   |d         s||d<   d S d S )	NTICKET_MODIFYz0%s doesn't have TICKET_MODIFY permission for #%dFclosedstatusfixed
resolutionowner)rk   check_permsrm   infor~   )r(   r   r6   rv   ru   s        r+   	cmd_closezCommitTicketUpdater.cmd_close  s    >>),, 	t ; ;HMML"FI/ / /5#x&|g 	'&F7OOO	' 	'r-   c                     | j         r:d|vr8| j                            d|                     |          |j                   dS d S d S )NTICKET_APPENDz0%s doesn't have TICKET_APPEND permission for #%dF)r   rm   r   rk   r~   )r(   r   r6   rv   s       r+   rT   zCommitTicketUpdater.cmd_refs  s]     	t ; ;HMML..33VY@ @ @5	 	 ; ;r-   )!__name__
__module____qualname____doc__r   r   r   r%   commands_closerR   r   r   r}   ticket_prefixticket_referencer'   propertyr,   r#   r&   rU   rI   r9   rD   r0   r1   r3   r4   rr   rN   rk   r   rT   r<   r-   r+   r   r   ;   s       1 1f J()))vh ?	?@ @H VH&K-EG GN F8%I*	:; ;M *X'I	 K Z"?EG GF 6M$AB ('')9)9)9;N = = X=
 
=:566IMM M M
M 
M 
M    2 2 2K K K,
4 
4 
4	 	 	% % %	' 	' 	'    r-   r   c                   >    e Zd ZdZ ed          Zej        ZddZdS )CommitTicketReferenceMacromessagesay  Insert a changeset message into the output.

    This macro must be called using wiki processor syntax as follows:
    {{{
    {{{
    #!CommitTicketReference repository="reponame" revision="rev"
    }}}
    }}}
    where the arguments are the following:
     - `repository`: the repository containing the changeset
     - `revision`: the revision of the desired changeset
    Nc           
      P   |pi }|                     d          pd}|                     d          }t          | j                                      |          }	 |                    |          }|j        }	|j        }|j        }
n"# t          $ r |}	t          d|          }
Y nw xY w|j
        j        j        dk    rvt          |j
        j        j                  r3t          fd| j                            |	          D                       s#t#          j        t'          d          d          S t)          | j                  j        rGt#          j        t/          | j        |j
                            d	||

          |	d          d          S t#          j        |	d          S )N
repositoryr   revisionr   c              3   >   K   | ]}t          |          k    V  d S r/   )rX   )r=   r`   resource_ids     r+   r@   z:CommitTicketReferenceMacro.expand_macro.<locals>.<genexpr>E  sJ       *F *F! +.f++*D *F *F *F *F *F *Fr-   z5(The changeset message doesn't reference this ticket)hint)class_r6   )parentT)escape_newlinesr2   )rP   r   rl   get_repositoryget_changesetr2   rF   rp   rs   r	   contextrealmr   r~   anyrU   rV   r   pr   r   wiki_format_messagesdivr   childpre)r(   	formatternamecontentargsre   rF   r5   r6   r2   rp   r   s              @r+   expand_macroz'CommitTicketReferenceMacro.expand_macro5  s   zr88L))/Rhhz""!$(++::8DD	&++C00I
  'G-C~HH  	8 	8 	8Gh77HHH	8 %+x77 !2!;!>??K ?c *F *F *F *F%)^%;%;G%D%D*F *F *F 'F 'F ?uQ  . / /7=? ? ? ?48$$9 	67>$(!''S'JJ/ / /7@B B B B 7795555s   B B$#B$r/   )	r   r   r   _domainr   _descriptionr   rU   r   r<   r-   r+   r   r   #  sM        G9	 	L $-I6 6 6 6 6 6r-   r   ))r#   rf   trac.configr   r   	trac.corer   r   trac.notification.apir   	trac.permr   trac.resourcer	   trac.ticketr
   trac.ticket.notificationr   	trac.utilr   trac.util.datefmtr   r   trac.util.htmlr   trac.util.textr   trac.util.translationr   r   trac.versioncontrolr   r   $trac.versioncontrol.web_ui.changesetr   trac.wiki.formatterr   trac.wiki.macrosr   r   r   r<   r-   r+   <module>r      s  L 
			  * * * * * * * * + + + + + + + + 4 4 4 4 4 4 % % % % % % " " " " " "       6 6 6 6 6 6       / / / / / / / /       / / / / / / . . . . . . . . L L L L L L L L @ @ @ @ @ @ . . . . . . * * * * * *e e e e e) e e eP+6 +6 +6 +6 +6 +6 +6 +6 +6 +6r-   