o k`~@sXddlZddlZddlZddlmZddlmZddlmZmZm Z m Z m Z m Z ddl TddlmZmZmZddlmZddlmZmZmZdd lmZmZdd lmZdd lmZmZdd l m!Z!m"Z"m#Z#m$Z$m%Z%dd l&m'Z'm(Z(Gddde)Z*Gddde+Z,Gddde+Z-Gddde+Z.Gddde+Z/Gddde0Z1ej2dddZ3dS)N)datetime)cached) BoolOption ConfigSection IntOption ListOptionOptionOrderedExtensionsOption)*)IPermissionRequestorPermissionCachePermissionSystem)IResourceManager)Rangesas_boolas_int) parse_date user_time)tag) shorten_line to_unicode)_N_ deactivategettext reactivate)IWikiSyntaxProvider WikiParsercsTeZdZdZdgZfddZfddZddd Zd d Zd d Z ddZ Z S)TicketFieldListz4Improved ticket field list, allowing access by name._mapcs tj|dd|D|_dS)NcSsi|]}|d|qSname.0valuer"r"1/usr/lib/python3/dist-packages/trac/ticket/api.py +sz,TicketFieldList.__init__..)super__init__r)selfargs __class__r"r&r))s zTicketFieldList.__init__cst|||j|d<dS)Nr!)r(appendr)r*r%r,r"r&r.-s zTicketFieldList.appendNcCs|j||SN)rget)r*r!defaultr"r"r&by_name1szTicketFieldList.by_namecCst|Sr/rr*r"r"r&__copy__4szTicketFieldList.__copy__cstfdd|DS)Nc3s|] }t|VqdSr/)copydeepcopyr#memor"r& 8sz/TicketFieldList.__deepcopy__..r3)r*r9r"r8r& __deepcopy__7szTicketFieldList.__deepcopy__cCs ||jvSr/)r)r*r!r"r"r& __contains__:s zTicketFieldList.__contains__r/) __name__ __module__ __qualname____doc__ __slots__r)r.r2r5r;r< __classcell__r"r"r,r&r%s   rc@8eZdZdZddZddZddZdd Zd d Zd S) ITicketActionControllerzExtension point interface for components willing to participate in the ticket workflow. This is mainly about controlling the changes to the ticket ''status'', though not restricted to it. cCdS)aiReturn an iterable of `(weight, action)` tuples corresponding to the actions that are contributed by this component. The list is dependent on the current state of the ticket and the actual request parameter. `action` is a key used to identify that particular action. (note that 'history' and 'diff' are reserved and should not be used by plugins) The actions will be presented on the page in descending order of the integer weight. The first action in the list is used as the default action. When in doubt, use a weight of 0. Nr"reqticketr"r"r&get_ticket_actionsFz*ITicketActionController.get_ticket_actionscCrE)a Returns an iterable of all the possible values for the ''status'' field this action controller knows about. This will be used to populate the query options and the like. It is assumed that the terminal status of a ticket is 'closed'. Nr"r"r"r"r&get_all_statusWrJz&ITicketActionController.get_all_statuscCrE)aReturn a tuple in the form of `(label, control, hint)` `label` is a short text that will be used when listing the action, `control` is the markup for the action control and `hint` should explain what will happen if this action is taken. This method will only be called if the controller claimed to handle the given `action` in the call to `get_ticket_actions`. Note that the radio button for the action has an `id` of `"action_%s" % action`. Any `id`s used in `control` need to be made unique. The method used in the default ITicketActionController is to use `"action_%s_something" % action`. Nr"rGrHactionr"r"r&render_ticket_action_control_rJz4ITicketActionController.render_ticket_action_controlcCrE)aLReturn a dictionary of ticket field changes. This method must not have any side-effects because it will also be called in preview mode (`req.args['preview']` will be set, then). See `apply_action_side_effects` for that. If the latter indeed triggers some side-effects, it is advised to emit a warning (`trac.web.chrome.add_warning(req, reason)`) when this method is called in preview mode. This method will only be called if the controller claimed to handle the given `action` in the call to `get_ticket_actions`. Nr"rLr"r"r&get_ticket_changesorJz*ITicketActionController.get_ticket_changescCrE)aPerform side effects once all changes have been made to the ticket. Multiple controllers might be involved, so the apply side-effects offers a chance to trigger a side-effect based on the given `action` after the new state of the ticket has been saved. This method will only be called if the controller claimed to handle the given `action` in the call to `get_ticket_actions`. Nr"rLr"r"r&apply_action_side_effects}rJz1ITicketActionController.apply_action_side_effectsN) r=r>r?r@rIrKrNrOrPr"r"r"r&rD>s rDc@rC) ITicketChangeListenerzvExtension point interface for components that require notification when tickets are created, modified, or deleted.cCrE)z Called when a ticket is created.Nr"rHr"r"r&ticket_createdrJz$ITicketChangeListener.ticket_createdcCrE)zCalled when a ticket is modified. `old_values` is a dictionary containing the previous values of the fields that have changed. Nr")rHcommentauthor old_valuesr"r"r&ticket_changedrJz$ITicketChangeListener.ticket_changedcCrE)z Called when a ticket is deleted.Nr"rRr"r"r&ticket_deletedrJz$ITicketChangeListener.ticket_deletedcCrE)z)Called when a ticket comment is modified.Nr")rHcdaterUrT old_commentr"r"r&ticket_comment_modifiedrJz-ITicketChangeListener.ticket_comment_modifiedcCrE)zCalled when a ticket change is deleted. `changes` is a dictionary of tuple `(oldvalue, newvalue)` containing the ticket change of the fields that have changed.Nr")rHrYchangesr"r"r&ticket_change_deletedrJz+ITicketChangeListener.ticket_change_deletedN) r=r>r?r@rSrWrXr[r]r"r"r"r&rQs rQc@(eZdZdZddZddZddZdS) ITicketManipulatorz7Miscellaneous manipulation of ticket workflow features.cCrE)zNNot currently called, but should be provided for future compatibility.Nr")rGrHfieldsactionsr"r"r&prepare_ticketrJz!ITicketManipulator.prepare_ticketcCrE)aValidate ticket properties when creating or modifying. Must return a list of `(field, message)` tuples, one for each problem detected. `field` can be `None` to indicate an overall problem with the ticket. Therefore, a return value of `[]` means everything is OK.Nr"rFr"r"r&validate_ticketrJz"ITicketManipulator.validate_ticketcCrE)zValidate ticket comment when appending or editing. Must return a list of messages, one for each problem detected. The return value `[]` indicates no problems. :since: 1.3.2 Nr")rGrTr"r"r&validate_commentrJz#ITicketManipulator.validate_commentN)r=r>r?r@rbrcrdr"r"r"r&r_s  r_c@r^) IMilestoneChangeListenerzyExtension point interface for components that require notification when milestones are created, modified, or deleted.cCrE)z#Called when a milestone is created.Nr" milestoner"r"r&milestone_createdrJz*IMilestoneChangeListener.milestone_createdcCrE)zCalled when a milestone is modified. `old_values` is a dictionary containing the previous values of the milestone properties that changed. Currently those properties can be 'name', 'due', 'completed', or 'description'. Nr")rgrVr"r"r&milestone_changedrJz*IMilestoneChangeListener.milestone_changedcCrE)z#Called when a milestone is deleted.Nr"rfr"r"r&milestone_deletedrJz*IMilestoneChangeListener.milestone_deletedN)r=r>r?r@rhrirjr"r"r"r&res  rec@seZdZeeeeeee Z ee Z dZ eddZeddeddddZedd d d Zedd d dZeddddZeddddZeddd dZeddd dZeddd dZeddd dZeddd dZeddd d Zedd!d"d#Zedd$d d%Z edd&d'd(Z!e"dd)d*d+d,Z#e$dd-d.d/Z%e$dd0d.d1Z&e$dd2d.d3Z'd4d5Z(d6d7Z)d8d9Z*d:d;Z+dd?Z-e.d@dAZ/gdBZ0dCdDZ1e.dEdFZ2dGdHZ3dfdJdKZ4dfdLdMZ5dNdOZ6dPdQZ7dRdSZ8dTdUZ9dVdWZ:dXdYZ;dfdZd[Zdgd`daZ?dhdbdcZ@dddeZAdIS)i TicketSystemrHz ticket-customztIn this section, you can define additional fields for tickets. See TracTicketsCustomFields for more details.workflowConfigurableTicketWorkflowFzLOrdered list of workflow controllers to use for ticket actions. )r1include_missingdocrestrict_ownerfalseaMake the owner field of tickets use a drop-down menu. Be sure to understand the performance implications before activating this option. See [TracTickets#Assign-toasDrop-DownList Assign-to as Drop-Down List]. Please note that e-mail addresses are '''not''' obfuscated in the resulting drop-down menu, so this option should not be used if e-mail addresses must remain protected. default_versionz*Default version for newly created tickets. default_typedefectz'Default type for newly created tickets.default_prioritymajorz+Default priority for newly created tickets.default_milestonez,Default milestone for newly created tickets.default_componentz,Default component for newly created tickets.default_severityz+Default severity for newly created tickets.default_summaryz2Default summary (title) for newly created tickets.default_descriptionz.Default description for newly created tickets.default_keywordsz+Default keywords for newly created tickets. default_ownerz < default >zzDefault owner for newly created tickets. The component owner is used when set to the value `< default >`. default_ccz+Default cc: list for newly created tickets.default_resolutionfixedz3Default resolution for resolving (closing) tickets.allowed_empty_fieldszmilestone, versionz_Comma-separated list of `select` fields that can have an empty value. (//since 1.1.2//))romax_comment_sizeiz+Maximum allowed comment size in characters.max_description_sizez/Maximum allowed description size in characters.max_summary_sizez=Maximum allowed summary size in characters. (//since 1.0.2//)cCs|jddd|jDdS)Nz*action controllers for ticket workflow: %rcSsg|]}|jjqSr")r-r=)r$cr"r"r& #z)TicketSystem.__init__..)logdebugaction_controllersr4r"r"r&r)!szTicketSystem.__init__c Cszi}|jD]#}|||pg}|D]\}}||vr#t|||||<q|||<qqdd|D}ddt|ddDS)z*Returns a sorted list of available actionscSsg|]\}}||fqSr"r")r$rMweightr"r"r&r2sz6TicketSystem.get_available_actions..cSg|]}|dqS)r")r$xr"r"r&r4rT)reverse)rrImaxitemssorted) r*rGrHra controllerweighted_actionsrrMall_weighted_actionsr"r"r&get_available_actions's   z"TicketSystem.get_available_actionscCs,t}|jD] }||pgqt|S)zYReturns a sorted list of all the states all of the action controllers know about.)setrupdaterKr)r* valid_statesrr"r"r&rK6s zTicketSystem.get_all_statuscCs"dd|D}td|d<|S)z8Produce a (name,label) mapping from `get_ticket_fields`.cSsi|] }|d|dqS)r!labelr"r$fr"r"r&r'@z8TicketSystem.get_ticket_field_labels.. Attachment attachment)get_ticket_fieldsr)r*labelsr"r"r&get_ticket_field_labels>s z$TicketSystem.get_ticket_field_labelscCs8t|j}d}|D]}|dst||||<q |S)a7Returns list of fields available for tickets. Each field is a dict with at least the 'name', 'label' (localized) and 'type' keys. It may in addition contain the 'custom' key, the 'optional' and the 'options' keys. When present 'custom' and 'optional' are always `True`. rcustom)r6r7r`r0r)r*r`rrr"r"r&rDs  zTicketSystem.get_ticket_fieldscCs|`dS)zInvalidate ticket field cache.N)r`r4r"r"r&reset_ticket_fieldsSsz TicketSystem.reset_ticket_fieldsc Csddlm}t}|ddtdd|ddtdd|d dtd d|d d d tdddtd|jfdtd|jfdtd|jfdtd|jfdtd|j fdtd|j fdtd|j fdtd|j fg}|D]<\}}}d d!| |jD}|sqr|d"|t|d#|d$|d%}|d&vrd'|d<d(|d)<n ||jvrd(|d)<||qr|d*dd+td,d|d-dd+td.d|d/d/d0td1d|d2d/d0td3d|jD]}|d4d5d!|Dvr|jd6|d4q||q|S)7z0Return the list of fields available for tickets.r)modelsummarytextSummary)r!typerreporterReporterownerOwner descriptiontextareawiki Description)r!rformatrrTypestatusStatuspriorityPriorityrg Milestone component ComponentversionVersionseveritySeverity resolution ResolutioncSsg|]}|jqSr"r )r$valr"r"r&rwsz'TicketSystem.fields..selectdefault_rs)r!rrr%options)rrradioToptionalkeywordslistKeywordsccCctimerelativeCreated changetimeModifiedr!cSrr r"rr"r"r&rrz$Duplicate field name "%s" (ignoring)) trac.ticketrrr.rrrrrrrrrrenvgetattrr custom_fieldsrwarning) r*rr`selectsr!rclsrfieldr"r"r&r`Wsr        zTicketSystem.fields)reportorderdescgroup groupdesccolrowrrpageverboserToridrrrrrrrrcCs t|jSr/)r6r7rr4r"r"r&get_custom_fieldss zTicketSystem.get_custom_fieldsc sBt}|jddDD]ddddp/ddd d d fd d }ddksMddkrjdddd<ds]qd dvsjd|j vr{dd<d dvr{d d |nnddkrt drdndd<|nXddkrddd<ddd <dd!vr|n4dd"kr݈ddd<ddd <d#d$<ndd%krdd&d<d|j vr|j d'dqtd(ds|j d)dq|q|jd*d+d,|S)-z>Return the list of custom ticket fields available for tickets.cSsg|] \}}d|vr|qS).r")r$optionr%r"r"r&rs z.TicketSystem.custom_fields..Tz.orderrz.labelr z.valuers)r!rrrrr%csddd<dS)Nz.ticketlink_queryticketlink_query)r0r"configrr!r"r&_get_ticketlink_querys z9TicketSystem.custom_fields.._get_ticketlink_queryrrrz.options|)seprr!rcheckboxr%10rz.formatplainrz .max_sizemax_size) referencerrz.rowsheightrrz-Field name "%s" is a reserved name (ignoring)z^[a-zA-Z][a-zA-Z0-9_]+$z.Invalid name for custom field: "%s" (ignoring)cSs|d|dfS)Nrr!r")rr"r"r&sz,TicketSystem.custom_fields..)key)rticket_custom_sectionrr0getintreplacestrip capitalizegetlistrremoverreserved_field_namesrrrematchr.sort)r*r`rr"rr&rsd         zTicketSystem.custom_fieldscCs dddS)zReturn a mapping from field name synonyms to field names. The synonyms are supposed to be more intuitive for custom queries.rr)createdmodifiedr"r4r"r"r&get_field_synonymss zTicketSystem.get_field_synonymsNcCs,|jrd|d<|||d<d|d<dSdS)z|Restrict given owner field to be a list of users having the TICKET_MODIFY permission (for the given ticket) rrrTrN)rpget_allowed_owners)r*rrHr"r"r&eventually_restrict_owners  z&TicketSystem.eventually_restrict_ownercCsR|jr'g}t|jdD]}|rdt|j||jvr ||q ||SdS)a_Returns a list of permitted ticket owners (those possessing the TICKET_MODIFY permission). Returns `None` if the option `[ticket]` `restrict_owner` is `False`. If `ticket` is not `None`, fine-grained permission checks are used to determine the allowed owners for the specified resource. :since: 1.0.3 TICKET_MODIFYN)rpr rget_users_with_permissionr resourcer.r)r*rHallowed_ownersuserr"r"r&rs   zTicketSystem.get_allowed_ownerscCsdSr/r")r*rGrHr`rar"r"r&rbszTicketSystem.prepare_ticketc cs|jD]<}d|vr q|d}|dkrq||vr@||jvr@||}|r3||dvr2|td|dfVq|dds@|tdfVqt|d pGd |jkrVd td |jd fV|d sbd tdfVnt|d phd |jkrwd td |jd fV|jD]0}|j|}|dd}d|krt||pd krnqz|d}|p|td |d fVqz|j D]Y}||}||jvr||jvrt |t s|j|}|d} z|rt |t || dnd||<Wqty} z|||<|d}|p|t| fVWYd} ~ qd} ~ wwqdS)Nrr!rz "%(value)s" is not a valid value)r%rFzfield cannot be emptyrrs0Must be less than or equal to %(num)s charactersnumrzTickets must contain a summary.rrrr)hint)r`_oldrr0lenrrrr2 time_fields isinstancerrr TracErrorr) r*rGrHrr!r% field_attrsrrrer"r"r&rc sx        $           zTicketSystem.validate_ticketccs,t|pd|jkrtd|jdVdSdS)Nrsr r)rrr)r*rGrTr"r"r&rdBs  zTicketSystem.validate_commentc Cs&ddddddddddgfd gd fg S) N TICKET_APPEND TICKET_CREATETICKET_CHGPROP TICKET_VIEWTICKET_EDIT_CCTICKET_EDIT_DESCRIPTIONTICKET_EDIT_COMMENTr TICKET_ADMIN)rrrrrrr"r4r"r"r&get_permission_actionsJs   z#TicketSystem.get_permission_actionscCs$d|jfd|jfd|jfd|jfgS)NbugissuerHrT) _format_link_format_comment_linkr4r"r"r&get_link_resolversVs zTicketSystem.get_link_resolversc#s$dtjtjffddfVdS)Nz!?(?%s)%scs|d|dd||S)NrHr)r#)ryzr4r"r&rcrz.TicketSystem.get_wiki_syntax..)rINTERTRAC_SCHEMErRE_STRr4r"r4r&get_wiki_syntax\s  zTicketSystem.get_wiki_syntaxcCsd|||||}|r |Sz||\}}} t|} t| dkrr| j} ||j| } ddlm} | | rqd| | vrq|j dt | fD]+\}}}}|||||}d| |f}|j| || }tj|||d|dWSn.t | }|rd |dd}|d d }|d d }tj|td |d|jj|d|dWSWn tyYnwtj|ddS)NrrTicketrz SELECT type, summary, status, resolution FROM ticket WHERE id=%s z#%s: %sz %s ticket)titlehrefclass_&,u,​z, zTickets %(ranges)s)ranges)r)r-r.missing ticket)r/)shorthand_intertrac_helper split_linkrrar realmtrac.ticket.modelr, id_is_validpermrdb_querystrformat_summaryr.rHrrrquery ValueError)r* formatternstargetr fullmatch intertraclinkparamsfragmentrrrHr,rrrrrr-r.r2 label_wrap ranges_wrapr"r"r&r#esR           zTicketSystem._format_linkcCsd}d|vr9|d}t|dkr8|\}}} |dkr'|r'|ds'|\}} }t| d} |dvr2d}||| }n|j}|}|r|jr|j|jkr|r|sS|dkrd} } } ||rddlm } | |j |j}|dkr{| |s{t d} d } ndd | |vr|j|jd |} |j|jjkr||d |d |d|d}|dkrt d|j|d} n t d||j|d} |d d} n|dkrt dnt d|d} d} n t d} d} nt d} d } tj|| | | dS|S)N:rr)r!r"rHr+zticket comment does not existr3rz #comment:%srrrrz$Description for #%(id)s: %(summary)s)rrz)Comment %(cnum)s for #%(id)s: %(summary)s)cnumrrz ticketrzComment %(cnum)s)rMzno permission to view ticketzforbidden ticketzticket does not exist)r/r.r-)splitrisdigitrr rr7resource_existsr8r,r get_changerr:r.rHr=rr6)r*r@rArBrr eltsrMr7rr.r-r/r,rHrr"r"r&r$st           z!TicketSystem._format_comment_linkccs|jVdSr/)r7r4r"r"r&get_resource_realmss z TicketSystem.get_resource_realmsc s^|dkr d|jS|dkr(ddlm}||j|jfdddD}|j|Std |jd S) Ncompactz#%srrr+csg|]}|qSr"r"rrRr"r&rrz9TicketSystem.get_resource_description..)rrrrzTicket #%(shortname)s) shortname)rr8r,rr=r)r*r rcontextkwargsr,r+r"rRr&get_resource_descriptions   z%TicketSystem.get_resource_descriptioncCsDt|}|r |d|}|r |dkr|r|d|7}d||fS|S)Nz: closedz%s (%s))r)r*rrrrr"r"r&r=s    zTicketSystem.format_summaryc Cslzt|j}Wn ttfyYdSw|jd|fr4|jdur#dS|jd|f}|dd|jkSdS)a >>> from trac.test import EnvironmentStub >>> from trac.resource import Resource, resource_exists >>> env = EnvironmentStub() >>> resource_exists(env, Resource('ticket', 123456)) False >>> from trac.ticket.model import Ticket >>> t = Ticket(env) >>> int(t.insert()) 1 >>> resource_exists(env, t.resource) True Fz!SELECT id FROM ticket WHERE id=%sNTz` SELECT count(DISTINCT time) FROM ticket_change WHERE ticket=%s r)intr TypeErrorr?rr;r)r*r id_revcountr"r"r&rPs zTicketSystem.resource_existsr/)NN)NNN)Br=r>r? implementsr rrr_ExtensionPointrQchange_listenersremilestone_change_listenersr7rrr rDrrrprrrrtrvrxryrzr{r|r}r~rrrrrrrrr)rrKrrrrr`rrrrrrrbrcrdr r%r*r#r$rSrXr=rPr"r"r"r&rks  C ;  9  (9  rkccsft}|durt|j}|j}||_zdVW|dur!||_t|dS|dur.||_t|wr/)rrkrr`rr)rHttstranslated_fieldsr"r"r&translation_deactivateds    rer/)4 contextlibr6rr trac.cacher trac.configrrrrrr trac.core trac.permr r r trac.resourcer trac.utilrrrtrac.util.datefmtrrtrac.util.htmlrtrac.util.textrrtrac.util.translationrrrrr trac.wikirrrr InterfacerDrQr_rerrkcontextmanagerrer"r"r"r&s6     K4