From e46a889ad6106e56759e19baafc006ce4305acf2 Mon Sep 17 00:00:00 2001 From: btey Date: Mon, 15 Mar 2021 11:20:24 +0100 Subject: [PATCH 01/69] Initial commit --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..f288702d2fa1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 3ccf3fa969b221e4e45afd63cf37478e09d04e0f Mon Sep 17 00:00:00 2001 From: Benjamin Tey Date: Wed, 17 Mar 2021 09:26:19 +0100 Subject: [PATCH 02/69] Initial release --- config/locales/crowdin/ar.yml | 73 +++++ config/locales/crowdin/bg.yml | 73 +++++ config/locales/crowdin/ca.yml | 73 +++++ config/locales/crowdin/cs.yml | 73 +++++ config/locales/crowdin/da.yml | 73 +++++ config/locales/crowdin/de.yml | 73 +++++ config/locales/crowdin/el.yml | 73 +++++ config/locales/crowdin/es.yml | 73 +++++ config/locales/crowdin/fi.yml | 73 +++++ config/locales/crowdin/fil.yml | 73 +++++ config/locales/crowdin/fr.yml | 73 +++++ config/locales/crowdin/hr.yml | 73 +++++ config/locales/crowdin/hu.yml | 73 +++++ config/locales/crowdin/id.yml | 73 +++++ config/locales/crowdin/it.yml | 73 +++++ config/locales/crowdin/ja.yml | 73 +++++ config/locales/crowdin/ko.yml | 73 +++++ config/locales/crowdin/lt.yml | 73 +++++ config/locales/crowdin/nl.yml | 73 +++++ config/locales/crowdin/no.yml | 73 +++++ config/locales/crowdin/pl.yml | 73 +++++ config/locales/crowdin/pt.yml | 73 +++++ config/locales/crowdin/ro.yml | 73 +++++ config/locales/crowdin/ru.yml | 73 +++++ config/locales/crowdin/sk.yml | 73 +++++ config/locales/crowdin/sl.yml | 73 +++++ config/locales/crowdin/sv.yml | 73 +++++ config/locales/crowdin/tr.yml | 73 +++++ config/locales/crowdin/uk.yml | 73 +++++ config/locales/crowdin/vi.yml | 73 +++++ config/locales/crowdin/zh-CN.yml | 73 +++++ config/locales/crowdin/zh-TW.yml | 73 +++++ config/locales/de.yml | 80 +++++ config/locales/en.yml | 80 +++++ lib/open_project/gitlab_integration.rb | 33 ++ lib/open_project/gitlab_integration/engine.rb | 62 ++++ .../gitlab_integration/hook_handler.rb | 62 ++++ .../notification_handlers.rb | 284 ++++++++++++++++++ lib/openproject-gitlab_integration.rb | 29 ++ openproject-gitlab_integration.gemspec | 15 + 40 files changed, 2981 insertions(+) create mode 100644 config/locales/crowdin/ar.yml create mode 100644 config/locales/crowdin/bg.yml create mode 100644 config/locales/crowdin/ca.yml create mode 100644 config/locales/crowdin/cs.yml create mode 100644 config/locales/crowdin/da.yml create mode 100644 config/locales/crowdin/de.yml create mode 100644 config/locales/crowdin/el.yml create mode 100644 config/locales/crowdin/es.yml create mode 100644 config/locales/crowdin/fi.yml create mode 100644 config/locales/crowdin/fil.yml create mode 100644 config/locales/crowdin/fr.yml create mode 100644 config/locales/crowdin/hr.yml create mode 100644 config/locales/crowdin/hu.yml create mode 100644 config/locales/crowdin/id.yml create mode 100644 config/locales/crowdin/it.yml create mode 100644 config/locales/crowdin/ja.yml create mode 100644 config/locales/crowdin/ko.yml create mode 100644 config/locales/crowdin/lt.yml create mode 100644 config/locales/crowdin/nl.yml create mode 100644 config/locales/crowdin/no.yml create mode 100644 config/locales/crowdin/pl.yml create mode 100644 config/locales/crowdin/pt.yml create mode 100644 config/locales/crowdin/ro.yml create mode 100644 config/locales/crowdin/ru.yml create mode 100644 config/locales/crowdin/sk.yml create mode 100644 config/locales/crowdin/sl.yml create mode 100644 config/locales/crowdin/sv.yml create mode 100644 config/locales/crowdin/tr.yml create mode 100644 config/locales/crowdin/uk.yml create mode 100644 config/locales/crowdin/vi.yml create mode 100644 config/locales/crowdin/zh-CN.yml create mode 100644 config/locales/crowdin/zh-TW.yml create mode 100644 config/locales/de.yml create mode 100644 config/locales/en.yml create mode 100644 lib/open_project/gitlab_integration.rb create mode 100644 lib/open_project/gitlab_integration/engine.rb create mode 100644 lib/open_project/gitlab_integration/hook_handler.rb create mode 100644 lib/open_project/gitlab_integration/notification_handlers.rb create mode 100644 lib/openproject-gitlab_integration.rb create mode 100644 openproject-gitlab_integration.gemspec diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml new file mode 100644 index 000000000000..44f4ee2918e9 --- /dev/null +++ b/config/locales/crowdin/ar.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +ar: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml new file mode 100644 index 000000000000..b4fdf7f5deed --- /dev/null +++ b/config/locales/crowdin/bg.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +bg: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml new file mode 100644 index 000000000000..140b08144c43 --- /dev/null +++ b/config/locales/crowdin/ca.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +ca: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml new file mode 100644 index 000000000000..6d8f297f8822 --- /dev/null +++ b/config/locales/crowdin/cs.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +cs: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml new file mode 100644 index 000000000000..0545f2c6e12f --- /dev/null +++ b/config/locales/crowdin/da.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +da: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml new file mode 100644 index 000000000000..e36fbc3514b4 --- /dev/null +++ b/config/locales/crowdin/de.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +de: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml new file mode 100644 index 000000000000..c0269806d7c2 --- /dev/null +++ b/config/locales/crowdin/el.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +el: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml new file mode 100644 index 000000000000..29dd7d62c964 --- /dev/null +++ b/config/locales/crowdin/es.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +es: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml new file mode 100644 index 000000000000..11dce857a10e --- /dev/null +++ b/config/locales/crowdin/fi.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +fi: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml new file mode 100644 index 000000000000..cc93fed5b82d --- /dev/null +++ b/config/locales/crowdin/fil.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +fil: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml new file mode 100644 index 000000000000..5efbb9c5c2d6 --- /dev/null +++ b/config/locales/crowdin/fr.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +fr: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml new file mode 100644 index 000000000000..d7fb39a13565 --- /dev/null +++ b/config/locales/crowdin/hr.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +hr: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml new file mode 100644 index 000000000000..f73bc81ea158 --- /dev/null +++ b/config/locales/crowdin/hu.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +hu: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml new file mode 100644 index 000000000000..1caca2337954 --- /dev/null +++ b/config/locales/crowdin/id.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +id: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml new file mode 100644 index 000000000000..4be3b75a45c8 --- /dev/null +++ b/config/locales/crowdin/it.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +it: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml new file mode 100644 index 000000000000..c8145a0f5950 --- /dev/null +++ b/config/locales/crowdin/ja.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +ja: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml new file mode 100644 index 000000000000..e1d8d4140228 --- /dev/null +++ b/config/locales/crowdin/ko.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +ko: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml new file mode 100644 index 000000000000..0758e84742b4 --- /dev/null +++ b/config/locales/crowdin/lt.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +lt: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml new file mode 100644 index 000000000000..16fa3ed5f4c4 --- /dev/null +++ b/config/locales/crowdin/nl.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +nl: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml new file mode 100644 index 000000000000..29178460bb6d --- /dev/null +++ b/config/locales/crowdin/no.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +"no": + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml new file mode 100644 index 000000000000..e47f53730eb5 --- /dev/null +++ b/config/locales/crowdin/pl.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +pl: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/pt.yml b/config/locales/crowdin/pt.yml new file mode 100644 index 000000000000..78016bcdd5e6 --- /dev/null +++ b/config/locales/crowdin/pt.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +pt: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml new file mode 100644 index 000000000000..c51762ddcfc2 --- /dev/null +++ b/config/locales/crowdin/ro.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +ro: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml new file mode 100644 index 000000000000..79d7107e480c --- /dev/null +++ b/config/locales/crowdin/ru.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +ru: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml new file mode 100644 index 000000000000..ee2d1b002f49 --- /dev/null +++ b/config/locales/crowdin/sk.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +sk: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml new file mode 100644 index 000000000000..d00171394732 --- /dev/null +++ b/config/locales/crowdin/sl.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +sl: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml new file mode 100644 index 000000000000..f8bbf7383c2c --- /dev/null +++ b/config/locales/crowdin/sv.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +sv: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml new file mode 100644 index 000000000000..ad4a49c64c64 --- /dev/null +++ b/config/locales/crowdin/tr.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +tr: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml new file mode 100644 index 000000000000..7b6f1e7b1cd7 --- /dev/null +++ b/config/locales/crowdin/uk.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +uk: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml new file mode 100644 index 000000000000..8c4d8e48ee46 --- /dev/null +++ b/config/locales/crowdin/vi.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +vi: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml new file mode 100644 index 000000000000..69a9026c4704 --- /dev/null +++ b/config/locales/crowdin/zh-CN.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +zh-CN: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml new file mode 100644 index 000000000000..1f59e50c02a2 --- /dev/null +++ b/config/locales/crowdin/zh-TW.yml @@ -0,0 +1,73 @@ +#-- copyright +#OpenProject is an open source project management software. +#Copyright (C) 2012-2020 the OpenProject GmbH +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License version 3. +#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +#Copyright (C) 2006-2017 Jean-Philippe Lang +#Copyright (C) 2010-2013 the ChiliProject Team +#This program is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public License +#as published by the Free Software Foundation; either version 2 +#of the License, or (at your option) any later version. +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +#You should have received a copy of the GNU General Public License +#along with this program; if not, write to the Free Software +#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#See docs/COPYRIGHT.rdoc for more details. +#++ +zh-TW: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/de.yml b/config/locales/de.yml new file mode 100644 index 000000000000..c337a83a8668 --- /dev/null +++ b/config/locales/de.yml @@ -0,0 +1,80 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +en: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 000000000000..c337a83a8668 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,80 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +en: + gitlab_integration: + merge_request_opened_comment: > + **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_closed_comment: > + **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_merged_comment: > + **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been merged by [%{gitlab_user}](%{gitlab_user_url}). + note_commit_referenced_comment: > + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + + %{commit_note} + note_mr_referenced_comment: > + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_mr_commented_comment: > + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + + %{mr_note} + note_issue_referenced_comment: > + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_issue_commented_comment: > + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in + Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + + %{issue_note} + note_snippet_referenced_comment: > + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + + %{snippet_note} + issue_opened_referenced_comment: > + **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been opened by [%{gitlab_user}](%{gitlab_user_url}). + issue_closed_referenced_comment: > + **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been closed by [%{gitlab_user}](%{gitlab_user_url}). + push_single_commit_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + + %{commit_note} diff --git a/lib/open_project/gitlab_integration.rb b/lib/open_project/gitlab_integration.rb new file mode 100644 index 000000000000..b06edeea833c --- /dev/null +++ b/lib/open_project/gitlab_integration.rb @@ -0,0 +1,33 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject + module GitlabIntegration + require "open_project/gitlab_integration/engine" + end +end diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb new file mode 100644 index 000000000000..8bf65c1ca944 --- /dev/null +++ b/lib/open_project/gitlab_integration/engine.rb @@ -0,0 +1,62 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'open_project/plugins' +require_relative './notification_handlers' +require_relative './hook_handler' + +module OpenProject::GitlabIntegration + class Engine < ::Rails::Engine + engine_name :openproject_gitlab_integration + + include OpenProject::Plugins::ActsAsOpEngine + + register 'openproject-gitlab_integration', + :author_url => 'https://github.com/btey/openproject-gitlab-integration', + bundled: true + + + initializer 'gitlab.register_hook' do + ::OpenProject::Webhooks.register_hook 'gitlab' do |hook, environment, params, user| + HookHandler.new.process(hook, environment, params, user) + end + end + + initializer 'gitlab.subscribe_to_notifications' do + ::OpenProject::Notifications.subscribe('gitlab.merge_request_hook', + &NotificationHandlers.method(:merge_request_hook)) + ::OpenProject::Notifications.subscribe('gitlab.note_hook', + &NotificationHandlers.method(:note_hook)) + ::OpenProject::Notifications.subscribe('gitlab.issue_hook', + &NotificationHandlers.method(:issue_hook)) + ::OpenProject::Notifications.subscribe('gitlab.push_hook', + &NotificationHandlers.method(:push_hook)) + end + + end +end diff --git a/lib/open_project/gitlab_integration/hook_handler.rb b/lib/open_project/gitlab_integration/hook_handler.rb new file mode 100644 index 000000000000..2b330ec9f8ac --- /dev/null +++ b/lib/open_project/gitlab_integration/hook_handler.rb @@ -0,0 +1,62 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + class HookHandler + # List of the gitlab events we can handle. + KNOWN_EVENTS = %w[push_hook issue_hook note_hook merge_request_hook].freeze + + # A gitlab webhook happened. + # We need to check validity of the data and send a Notification + # which we process in our NotificationHandler. + def process(hook, request, params, user) + event_type = request.env['HTTP_X_GITLAB_EVENT'] + event_type.gsub!(' ','_') + event_type = event_type.to_s.downcase + + Rails.logger.debug "Received gitlab webhook #{event_type}" + + return 404 unless KNOWN_EVENTS.include?(event_type) + return 403 unless user.present? + + payload = params[:payload] + .permit! + .to_h + .merge('open_project_user_id' => user.id, + 'gitlab_event' => event_type) + + OpenProject::Notifications.send(event_name(event_type), payload) + + return 200 + end + + private def event_name(gitlab_event_name) + "gitlab.#{gitlab_event_name}" + end + end +end diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/lib/open_project/gitlab_integration/notification_handlers.rb new file mode 100644 index 000000000000..23f0ff424e71 --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handlers.rb @@ -0,0 +1,284 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + + ## + # Handles gitlab-related notifications. + module NotificationHandlers + + def self.merge_request_hook(payload) + accepted_actions = %w[open] + accepted_states = %w[closed merged] + return unless (accepted_actions.include? payload['object_attributes']['action']) || (accepted_states.include? payload['object_attributes']['state']) + comment_on_referenced_work_packages payload['object_attributes']['title'], payload + + rescue => e + Rails.logger.error "Failed to handle merge_request_hook event: #{e} #{e.message}" + raise e + end + + def self.note_hook(payload) + comment_on_referenced_work_packages payload['object_attributes']['note'], payload + + rescue => e + Rails.logger.error "Failed to handle note_hook event: #{e} #{e.message}" + raise e + end + + def self.push_hook(payload) + push_hook_split_commits payload + + rescue => e + Rails.logger.error "Failed to handle push_hook event: #{e} #{e.message}" + raise e + end + + def self.issue_hook(payload) + comment_on_referenced_work_packages payload['object_attributes']['title'] + ' - ' + payload['object_attributes']['description'], payload + + rescue => e + Rails.logger.error "Failed to handle issue_hook event: #{e} #{e.message}" + raise e + end + + def self.push_hook_split_commits(payload) + return nil unless payload['object_kind'] == 'push' + payload[:commits].each do |commit| + # concatenated to check title and description in one step + text = commit['title'] + " - " + commit['message'] + comment_on_referenced_work_packages text, payload, commit + end + end + + def self.comment_on_referenced_work_packages(text, payload, commit = nil) + user = User.find_by_id(payload['open_project_user_id']) + wp_ids = extract_work_package_ids(text) + wps = find_visible_work_packages(wp_ids, user) + if payload['object_kind'] == 'push' + notes = notes_for_push_payload(commit, payload) + elsif payload['object_kind'] == 'issue' + notes = notes_for_issue_payload(payload) + elsif payload['object_kind'] == 'merge_request' + notes = notes_for_merge_request_payload(payload) + elsif payload['object_kind'] == 'note' + if wps.empty? && payload['object_attributes']['noteable_type'] == 'Issue' + wp_ids = extract_work_package_ids(payload['issue']['title'] + ' - ' + payload['object_attributes']['note']) + wps = find_visible_work_packages(wp_ids, user) + notes = notes_for_note_payload(payload, 'comment') + elsif wps.empty? && payload['object_attributes']['noteable_type'] == 'MergeRequest' + wp_ids = extract_work_package_ids(payload['merge_request']['title'] + ' - ' + payload['object_attributes']['note']) + wps = find_visible_work_packages(wp_ids, user) + notes = notes_for_note_payload(payload, 'comment') + else + notes = notes_for_note_payload(payload, 'reference') + end + else + return + end + + return if notes.nil? + + if payload['object_kind'] == 'merge_request' + if payload['object_attributes']['state'] == 'opened' + attributes = { journal_notes: notes, status_id: 7 } + elsif payload['object_attributes']['state'] == 'merged' + attributes = { journal_notes: notes, status_id: 8 } + else + attributes = { journal_notes: notes } + end + else + attributes = { journal_notes: notes } + end + + wps.each do |wp| + ::WorkPackages::UpdateService + .new(user: user, model: wp) + .call(attributes.merge(send_notifications: false).symbolize_keys) + end + end + + ## + # Parses the given source string and returns a list of work_package ids + # which it finds. + # WorkPackages are identified by their URL. + # Params: + # source: string + # Returns: + # Array + def self.extract_work_package_ids(source) + # matches the following things (given that `Setting.host_name` equals 'www.openproject.org') + # - http://www.openproject.org/wp/1234 + # - https://www.openproject.org/wp/1234 + # - http://www.openproject.org/work_packages/1234 + # - https://www.openproject.org/subdirectory/work_packages/1234 + # Or with the following prefix: OP# + # e.g.,: This is a reference to OP#1234 + host_name = Regexp.escape(Setting.host_name) + wp_regex = /OP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ + + source.scan(wp_regex) + .map {|first, second| (first || second).to_i } + .select { |el| el > 0 } + .uniq + end + + ## + # Given a list of work package ids this methods returns all work packages that match those ids + # and are visible by the given user. + # Params: + # - Array: An list of WorkPackage ids + # - User: The user who may (or may not) see those WorkPackages + # Returns: + # - Array + def self.find_visible_work_packages(ids, user) + ids.collect do |id| + WorkPackage.includes(:project).find_by_id(id) + end.select do |wp| + wp.present? && user.allowed_to?(:add_work_package_notes, wp.project) + end + end + + def self.notes_for_issue_payload(payload) + return nil unless payload['object_attributes']['action'] == 'open' || payload['object_attributes']['state'] == 'closed' + I18n.t("gitlab_integration.issue_#{payload['object_attributes']['state']}_referenced_comment", + :issue_number => payload['object_attributes']['iid'], + :issue_title => payload['object_attributes']['title'], + :issue_url => payload['object_attributes']['url'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + end + + def self.notes_for_push_payload(commit, payload) + commit_id = commit['id'] + I18n.t("gitlab_integration.push_single_commit_comment", + :commit_number => commit_id[0, 8], + :commit_note => commit['message'], + :commit_url => commit['url'], + :commit_timestamp => commit['timestamp'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user_name'], + :gitlab_user_url => payload['user_avatar']) + end + + def self.notes_for_merge_request_payload(payload) + key = { + 'opened' => 'opened', + 'reopened' => 'opened', + 'closed' => 'closed', + 'merged' => 'merged', + 'edited' => 'referenced', + 'referenced' => 'referenced' + }[payload['object_attributes']['state']] + + return nil unless key + + I18n.t("gitlab_integration.merge_request_#{key}_comment", + :mr_number => payload['object_attributes']['id'], + :mr_title => payload['object_attributes']['title'], + :mr_url => payload['object_attributes']['url'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['url'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + end + + def self.notes_for_note_payload(payload, note_type) + if payload['object_attributes']['noteable_type'] == 'Commit' + commit_id = payload['commit']['id'] + I18n.t("gitlab_integration.note_commit_referenced_comment", + :commit_id => commit_id[0, 8], + :commit_url => payload['object_attributes']['url'], + :commit_note => payload['object_attributes']['note'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + elsif payload['object_attributes']['noteable_type'] == 'MergeRequest' + if note_type == 'comment' + I18n.t("gitlab_integration.note_mr_commented_comment", + :mr_number => payload['merge_request']['id'], + :mr_title => payload['merge_request']['title'], + :mr_url => payload['object_attributes']['url'], + :mr_note => payload['object_attributes']['note'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + elsif note_type == 'reference' + I18n.t("gitlab_integration.note_mr_referenced_comment", + :mr_number => payload['merge_request']['id'], + :mr_title => payload['merge_request']['title'], + :mr_url => payload['object_attributes']['url'], + :mr_note => payload['object_attributes']['note'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + end + elsif payload['object_attributes']['noteable_type'] == 'Issue' + if note_type == 'comment' + I18n.t("gitlab_integration.note_issue_commented_comment", + :issue_number => payload['issue']['iid'], + :issue_title => payload['issue']['title'], + :issue_url => payload['object_attributes']['url'], + :issue_note => payload['object_attributes']['note'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + elsif note_type == 'reference' + I18n.t("gitlab_integration.note_issue_referenced_comment", + :issue_number => payload['issue']['iid'], + :issue_title => payload['issue']['title'], + :issue_url => payload['object_attributes']['url'], + :issue_note => payload['object_attributes']['note'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + end + elsif payload['object_attributes']['noteable_type'] == 'Snippet' + I18n.t("gitlab_integration.note_snippet_referenced_comment", + :snippet_number => payload['snippet']['id'], + :snippet_title => payload['snippet']['title'], + :snippet_url => payload['object_attributes']['url'], + :snippet_note => payload['object_attributes']['note'], + :repository => payload['repository']['name'], + :repository_url => payload['repository']['homepage'], + :gitlab_user => payload['user']['name'], + :gitlab_user_url => payload['user']['avatar_url']) + else + return nil + end + end + end +end diff --git a/lib/openproject-gitlab_integration.rb b/lib/openproject-gitlab_integration.rb new file mode 100644 index 000000000000..9e4cc60533a3 --- /dev/null +++ b/lib/openproject-gitlab_integration.rb @@ -0,0 +1,29 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2020 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'open_project/gitlab_integration' diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec new file mode 100644 index 000000000000..1552e73e483f --- /dev/null +++ b/openproject-gitlab_integration.gemspec @@ -0,0 +1,15 @@ +# encoding: UTF-8 +Gem::Specification.new do |s| + s.name = "openproject-gitlab_integration" + s.version = '1.0.0' + s.authors = "Benjamin Tey" + s.email = "ben.tey@outlook.com" + s.homepage = "https://github.com/btey/openproject-gitlab-integration" + s.summary = 'OpenProject GitLab Integration' + s.description = 'Integrates OpenProject and GitLab for a better workflow' + s.license = 'GPLv3' + + s.files = Dir["{app,config,db,doc,lib}/**/*"] + %w(README.md) + + s.add_dependency "openproject-webhooks" +end From 5493951334626d4536b2768e3d2effd255a6b35f Mon Sep 17 00:00:00 2001 From: btey Date: Wed, 17 Mar 2021 11:06:19 +0100 Subject: [PATCH 03/69] Initial readme --- README.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000000..65e035b56026 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# openproject-gitlab-integration +OpenProject module for integration with Gitlab + +This plugin is based on the current plugin to integrate Github with OpenProject (https://docs.openproject.org/system-admin-guide/github-integration). + +The reference system is the same as for GitHub integration. You can use a link to the work package or just use “OP#87” in the title or description in Gitlab. + +## Available events captured in OpenProject + +OpenProject will **add comments** to work package for the following events: +* Merge Request (Opened, Closed and Merged) +* Issue (Opened, Closed) +* Push commits in Merge Requests +* Comments (on Issues, Merge Request, Commits and Snippets) + +OpenProject will **update WP status** in this events: +* Merge Request (opened) - Status: In progress (currently ID=7) +* Merge Request (merged) - Status: Developed (currently ID=8) + +## Example workflow + +A typical workflow on Gitlab side would be: +1. **Create Issue.** +> **Issue Opened:** Issue 6 New contact form - OP#18 for Scrum project has been opened by Administrator. + +2. **Comment on issue.** +> **Commented in Issue:** Administrator commented this WP in Issue 6 New contact form - OP#18 on Scrum project: +> +> New comment on the issue with attachment. +3. **Create Merge Request.** +> **MR Opened:** Merge request 25 Draft: Resolve "New contact form - OP#18" for Scrum project has been opened by Administrator. +> +> **Status** changed from _Specified_ +> **to** _In progress_ +4. **Comment in Merge Request.** +> **Commented in MR:** Administrator commented this WP in Merge request 25 Draft: Resolve "New contact form - OP#18" on Scrum project: +> +> New comment on MR. +5. **Reference in other Issues (comments).** +> **Referenced in Issue:** Administrator referenced this WP in Issue 2 New backend pipeline on Scrum project: +> +> OP#18 New comment about... +6. **New commit in Merge Request.** +> **Pushed in MR:** Administrator pushed fca3d6fb to Scrum project at 2021-03-08T08:01:57+00:00: +> +> Update readme.md OP#18 +7. **Comment in a new commit of the Merge Request.** +> **Referenced in Commit:** Administrator referenced this WP in a Commit Note 0bf0e3e9 on Scrum project: +> +> This change is for OP#18. +8. **Merge Request merged (generates up to 3 events).** +> **Pushed in MR:** Administrator pushed 1da09cb4 to Scrum project at 2021-03-05T14:57:37+00:00: +> +> Merge branch '5-new-contact-form-op-18' into 'master' +> +> Resolve "New contact form - OP#18" +> +> Closes #6 +> +> See merge request root/scrum!9 + +> **MR Merged:** Merge request 24 Resolve "New contact form - OP#18" for Scrum project has been merged by Administrator. +> +> **Status** changed from _In progress_ +> **to** _Developed_ + +> **Issue Closed:** Issue 6 New contact form - OP#18 for Scrum project has been closed by Administrator. + +## Configuration + +You will have to configure both OpenProject and Gitlab for the integration to work. + +### OpenProject + +First you will need to create a user in OpenProject that will make the comments. The user will have to be added to each project with a role that allows them to comment on work packages and change status. + +Once the user is created you need to generate an OpenProject API token for it to use later on the Gitlab side: + +* Login as the newly created user. +* Go to My Account (click on Avatar in top right corner). +* Go to Access Token. +* Click on generate in the API row. +* Copy the generated key. You can now configure the necessary webhook in Gitlab. + +### Gitlab + +In Gitlab you have to set up a webhook in each repository to be integrated with OpenProject. + +You need to configure just two things in the webhook: +1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: +``` +http://openproject-url.com/webhooks/gitlab?key=ae278268 +``` +2. Enable the required triggers: Push events, Comments, Issues events, Merge request events + +Now the integration is set up on both sides and you can use it. From cb0949a3bfee216c1be73696ab4f71195a3d9458 Mon Sep 17 00:00:00 2001 From: btey Date: Wed, 17 Mar 2021 11:17:22 +0100 Subject: [PATCH 04/69] Update README.md --- README.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 65e035b56026..7476bfe11226 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # openproject-gitlab-integration -OpenProject module for integration with Gitlab +OpenProject module for integration with Gitlab (latest release tested is 13.9.3) This plugin is based on the current plugin to integrate Github with OpenProject (https://docs.openproject.org/system-admin-guide/github-integration). @@ -68,7 +68,25 @@ A typical workflow on Gitlab side would be: ## Configuration -You will have to configure both OpenProject and Gitlab for the integration to work. +You will have to configure both OpenProject and Gitlab for the integration to work. But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenProject detects the new module. + +Add the following in **Gemfile.lock**: +``` +PATH + remote: modules/gitlab_integration + specs: + openproject-gitlab_integration (1.0.0) + openproject-webhooks +``` + +Add the following in **Gemfile.modules**: +``` +group :opf_plugins do +... + gem 'openproject-gitlab_integration', path: 'modules/gitlab_integration' +... +end +``` ### OpenProject From af35aa63d5ba3e88341e49753715cf6c5227fced Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Thu, 25 Mar 2021 11:26:25 +0100 Subject: [PATCH 05/69] New images for readme. --- doc/op-commented-in-issue.png | Bin 0 -> 103658 bytes doc/op-commented-in-mr.png | Bin 0 -> 63583 bytes doc/op-issue-opened.png | Bin 0 -> 49835 bytes doc/op-mr-merged-event-1.png | Bin 0 -> 56609 bytes doc/op-mr-merged-event-2.png | Bin 0 -> 80789 bytes doc/op-mr-merged-event-3.png | Bin 0 -> 69892 bytes doc/op-mr-merged-event-4.png | Bin 0 -> 48612 bytes doc/op-mr-opened.png | Bin 0 -> 70424 bytes doc/op-pushed-in-mr.png | Bin 0 -> 55585 bytes doc/op-referenced-in-commit.png | Bin 0 -> 58901 bytes doc/op-referenced-in-issue.png | Bin 0 -> 57509 bytes 11 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/op-commented-in-issue.png create mode 100644 doc/op-commented-in-mr.png create mode 100644 doc/op-issue-opened.png create mode 100644 doc/op-mr-merged-event-1.png create mode 100644 doc/op-mr-merged-event-2.png create mode 100644 doc/op-mr-merged-event-3.png create mode 100644 doc/op-mr-merged-event-4.png create mode 100644 doc/op-mr-opened.png create mode 100644 doc/op-pushed-in-mr.png create mode 100644 doc/op-referenced-in-commit.png create mode 100644 doc/op-referenced-in-issue.png diff --git a/doc/op-commented-in-issue.png b/doc/op-commented-in-issue.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d0c13d15e83e1f3c5caa9846ea693b917d3a23 GIT binary patch literal 103658 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

lpm}_mI`B;c9+gOt7nZ^x(tZ>TfM8 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 literal 0 HcmV?d00001 diff --git a/doc/op-commented-in-mr.png b/doc/op-commented-in-mr.png new file mode 100644 index 0000000000000000000000000000000000000000..523918e91cfa861ea8ac8c4c1ca3d46af376ec28 GIT binary patch literal 63583 zcmeFYWmsK3x-d*z99rBd?(Xi+#$n^`R@{pfD8=2~ZR75~aVzd_#jUu#bY{-XJkL3Q zzkgq@D?3@qon);$w6Dah@DV)GUjfd-}yV}JhsSP-{6BGkYu@ZN|G@!Fg_utrM_C7 zok2)3uh*&UJlw9omRg?QDKkJQ()1kgQFCH#elp?P4<(0!$o@p^6Bh_&1a(VPMlB34 zcE<^2E`KQrRiwa_%7=o6wH?Lu8GP=ol>ZxLIH*r2js-NInBP-F(-g&^9E zb7X;#DjQqdO#dL{2C=O^2*v2>cj3PUcSoOOUYt#RK2fR+#d*tN3kXqR?Av2LXGval z(sN33)CY0148E6^3O5qmc{(-Q52V(N7oc4n1S>GBA7-)i@yQD%JKBt= zpHq(`Lsutwz%Mwcj3z<%%U6>zN;Iqg0B5oH;SYg3#J=y=9J?3jcbU^(VIgdx7Bwj* zGnt1LELIy%Grs^j#W3^AvKZlS($+Yo7CkF|D?Gz54%b8ZHzAvzr5`-plgv5GTgKLK z5C{_4^HAvFFfyF0oRW`+DED7E`6u{T8kRZQyt{OiuZ&R6G{mH2vkLV?7K8Y-F{N4r z`|Z7tl3y;$E8hi42$PDUkU4QO;U|I{Zmq=*|27ea&sd|&>ZCWodN zbLk_ykf1PScI{8oVyqUJYti1Hc)wV~{fDY3%8=4}YYm`B{d+gK+zHa*_Ivy6aoRpr z_#JJE+9P;jcEGRy*gZ#ZM~6}*L;q;{y+)K-@ugS_2v75YT$o;5H5;UKT}%Pw1tGYg zkxRCVd5aiN0@*&>1l@=PWp7XFI17BG{z~71=?$^!FA&ucaw-{>-DyaL%^&`;AFa2a z%#ei{fq6ICYE)p`jF2UX?lo?nv zNCgoxKS>QS4TS|63rZbIMAZJ^LotM0PUgsDaUJ0@Vh$8WWJ1*SSSd2&SQzS5GBSF~ z+33hl#7s1Uk}6(lbi8=(Cu0Wh!(h__$aR>g=*SHpOuz0u=#}qUy2g z0u5h+DUAyaJq;&NuLKo12Gj&ysfMYRsz#OQse6}5D6*Oxw67Q3T;-I zTIH)Mv&OyP#2<-aYW`|5Y8i9X^xPlCS&OPIx$Ca$^6Gf&CRl%)SDBrgm!(XyZTE*G zEwR?8EP0*4pO~F2FJ-y>;_>5Ixkk(md+>ML)Nk=O$+dTo)45&@bzhdrg0l`H=cHk%e-UB1SH)}Hp9_F7Gk7IRrm zxvR#tM(`T|n)5pChGAG(6 z`eQ5^2`tvzWfhw1_&T2*@o#we>}Tu|UFclCxOr`xoPb?GPA$8#4xb$L_Ez?umiqbw z&uXT3D`;ogdt4H%i;mPe-8e(E_aJCprv$*xuZ)9^K9#pj6Fd7eVe0w$H*Ts8bDE&%+3`C&W4p*(Tf1hYRm!n_7{cbt7{-CdwOhF9??c9? z#sbEg)ioMH8kJA@Ph>s=&jNRz4~!RQutHFfFhEFW7%Rwrm^o;HPb{B?5v>q$;aorJ z;jf_h!@(geVowdsnL90hJdRPk{Yjp)-#chnZCG!p&{N(^zIlRTFJ=KQ3=VP{-3#8U zlkk^v-6W>FI_bD4=feFIB3HPxY1x|(AOxx%>ScTb% z*>i|DMU8}vRCD<()uv+eONUy9dUhJnbpRs69r2UReol>}pJiR-=oQ*oCyewa+eWTj z_V-5x>xEq2nRi#S$!Zxbb=NVbOUd(_vt?v-@Oythnphq7?l!*UxcYIw_C)hii+C0e z3eS-}8kdNwr0NRK_T`5Ryed2~ywfkgSKYG#?Vu(r0~K&#;@IyojI?zd&8l6; zdYS8ra&UP~`DN;PYJ*MTGRK!kW!mC)-F@}>HeI{N)DuG2bJvxHp_Rot8DI6?iU~nj z-=>FD!eDOuj_&rH&Fr=A>j?Y2 zGKV|G!<@|CcR|ak$EmKI)2Xyk-$-jnCwu{q2ji1fc^L!P1F1e7j~hZmuk#imLl zQl;6dYl$m!o+Q7;%v;qTFZ7Rn7k#IGnXFQms~jnyDI{eO2&#CQ)rqcLTz=VG^c#oD zKpH2{Xmt^I@VX7GLP%nO*5#Y;JLGX1dYl>>$xKqvG1ReaKXk3%O+BjL?reGa+O}NY zy4&)2-9QkPd9$|IUSxN-#^uSnvDc@4tgUW0(Y)$O`7*xRF(YuVySAF+$$hK0&e$e+ zV{k7z+8Ziq6$K(Z;;(oOniWjlwY^k%e)l||2^R2R>iX<{k@mZhF~C%TTA?%xN5C4} z5toM>pKayJ>-4G{YYWSmal}{s)%%flL|yQ;`)PYgYHD!9dg$na(|Y}|ZS{40S9jlH z=GXRZ^Vxs^&}-)2eFJSEJQMMzY8_$vX9*qEs#y z#kY)%gl&C_F?-k4QE`cQ^1bc5J8f?W`x6MSgz%`S6KulQ7A)!;0*KG|ajANS8e$`y zI4r*FDXyi(=8#Wg6xT0VU!NWk(J`97bxyoOZu(+&cm5Y z68)Li(8$iknV*E@&qDuB|9nqVcgz2*Wb5>=&wBen#y>rb%nVG7|DOA8DBqt} z9z{!c(=QrgmNsuVds{<*n~9C@AMpRH=RYg{bEK-1siUZ!&D%g{f&U==UxWX<^X~)y zv83jImSo{#`R^tFtLI-M`56Cv`F~;JpJ@I^>l>a0;Q1K;y=MaO=StZAZ`+7(DW(8? zE8mFhA1UJPm+CG5Dc`dHnX`)YZwLq>2x&24p!>Vy3@8Kif!hInzU~|iYAAU2Ly1-0 z;#OrUm-~d4R$3RklGfJNDdmFq^rscoBfBqWtcDeyBdfa%aAZ%9dNQtdZmwoGtv4N4 zCXPllMn;X1tTzqS4ohS&gd z(iZZ64f)d<^bJL<73IGt`uDW>?w&Q-!T9GVt?C!|4-0Z9kL3mI%$Q&YMJ=N zZ}EijJR~a!9R4eXqf^2D@ zA8z0XQ*UlL2G*&aFyot9CUmD2)Q;AeZSI|UIZu79+ZZcX*%VtC*Ew`Av~Hg-Z^^hX zrzocrtt-SF^4QhlU%WXJuhoxI8_G`D{=yVYkcm)`&>wxo1@1TP?)P%03yPW6T|AgY1FcdZ>+uCD7^^*3IRhI^0g%fW9} z^;;{az0H1g@-*55VZ^j_`_#T4Kb)t22rmmM3E|9p1f%sOp5JtSOs(=H`tIciWiWG0 zGHx9RUMyFczAqP#OtP^gfu^Y0dO=hAiUc(bT`E$GhqQTDIh*{5l#63;m~W`WmOP<( zHda5CbkYogoN-B{(Qje2>Rc!S688-A_L29xy``)-Fg{W*I)~k5lT76Jh!QCE*S) zP7}q9!>WM)7C&z?$?psDxt_E8Cq0iR+tZ(RzIe0)x{v9{jjKG42VJ^TYMffVv!B|3 zF;lA~&5*jSOO5k*wbt$S^5Vr4iA-G*1p@|+08ftbM0-GM+4L9v0BS9F8xz>VmK} z?^_wMoB!PCT)1tZpQm**#^{qIxsFTIYO|&decEp6#rgPx%cf>~-+t9ZzgcIxpd?`Bb=vQzzecveDaS9$O({h*dG%hdgh73hsQ`-=*rSbs{MD#B)xAy@l_a7&A=o~ zY5CgZSZP?}RVW`=ajJ?rcJk1e^xz%K%LGs?{!mmx@ou>#IcxK4LgjA7erIsj`RpO? zlDe)Kp0b|$^rd*1?8r5F@XD9ky+pwNu2$a%(_U{mi~uS7FThF}X(iVH2%q2J#<*~z znzJ1|{dh(n+}%){j&Xovl$Npz-0gPyeZf?rX>I)eX4LBQ@5fnx-g($u7;xfW6{HNl zU)Y(aw%#;Ze3af-jTPSWV_v^Xv%w*8bE83dz}$yCh8+#Y#~kE~kf*cm!ZUcByd zMOSW|846X*#*iWSt;mx)zLQ#$1vRy|a%*kmLFc{coKJpQ!A7?pvSueSY+QPFhJ&|HA0qsH5^} z#ju@A^E>SNwzPWwj;+6SaqgcR1- zhv}f^x@EjbqMncJH|CGUt9dRBCkUpBmzK zCi22!>NT|tffF>jz&I`m#9#zRkjXeNqs+KB=Wq|hs!0NGW`d3j@0jqk-TU`By*#_S zk+nB*<@0B|zGn5up8X$Kd=By5MxXcR!RxDcV987HvS5B$kO_ZUp{COItw(Wwac=JJBdwxb_72VGuhcIJ1{W4km-uq|Qcu`k9zD2)1;}D6zR|Q{H9ZzBp$tkm=#j5!dEqbcH-0?QMw5;MAqp2tX72W%%h+9tShq4ZUG0o+#@KFx^TDfEb zOM=<)x_asOq}9%U#}z^R<@0OC7=Xo}OE|94>*`3uV|Q9VP>gHv##=3he&#ZW2?k!c zQi{g^*VsZ+;AtM`0#AElk^`V$@8K?~Nfk9oL&vxs5{_h<c?^Gp**0NTKH}uF0ofD`w|9GnZ9>vHN!M!H!pW{+ z@ZU^i3n|E-+bQElVJ_;itrI11X+}Q3pC`P*2Um@m9ST!>8<_UtsxB|i?ojwr1c)$6)|2G=8Lq)#?qxcrM}FlC5`Ea2@KRgVk`96Ndr@0D+!Cs+>qSxp<_0RtAa z2YkPbs28m~SBuQVj4++zrRS!b)XPzVn$Kn`#l4o0~U*oJ`_>J>}e` zcPSOCi4X?I_RBR4E%BoUr%z;yWi)|&?z=3@c;HaMKAyj0SU)YhP7=5xBRdeGq_UI`=#e$T91u4#SY?27E5K-AGDB6b5@=QDiiscJo}H^cKmmTrsN9 zG0lo-L@Hmp?2%m7=ffH`$-VL_V%*$a2q||ndHv%-{kN;pE9XmzvC^V#X?%} zs5bLLX5#g?{1!pP0G(j-S2s(e2Nuz1k9ADrq1=EP`~kW7lyU^8tY7ss#cNBZyjlS46)_7V&-6iiF|pBR3Q|wei~%HyV{(zStP$YT2t1py zX@a~}OywdisW{6GIK%X`Y_TN^$eh|)CugmB(s7&LQYGXo`FoHAs}lQEN3FB|#45rM zT<>x`J203|=reb~;pj3PmK<*(sMdZUJ)~Qmgua6*UC>Xp$IH3Ew-2rT4Wl+w6Igq2 z^mBb+sH-$9g0LqLuvddU?tF-(u>@JI6<{hUmrl)U#u%-6_$s@kYqtodnHSaNh*^Rq zQE}0+pGd1X2Iwvg(Q*=O@ap@~tzpVSZ5$9?B+3i5ee#%@+6Bzvlf$zG;)>NB_y zBMOQJS0R^5k5Ztr?cxa|lA3mY{EJ_f$o77q^yMMqu<~rhwT-SaQ7C_XSE1QsLa=$? z1kRkCe8%c%`^XhjB-gQN8u@Qito^yiV>VU-H*L}^q-OZ{34RTG+)mm5N?OLb z+ZLS|q<3tlsFtSuD1)pklUff<;+{+vOYo9wc{NM99alk}bc)#B_5lr9$Jf;_`nVnV zTqYdi7L&;tW3mrfw2K`S_Y>_69V_U^54o+;AQ)X0kfm0F72tAO92$OAgPc3>Er;fP zRIR^|&B+!nDzX-e>E_}GZ%xIuk9+-;`z#zL`|`3h9$gC$WD zp*Z+0(nZ@b`gyr78jD@&BXg(}2joQ{l5Zj{q0hwUq@fvt6jZQfE#{zn(c1RmZq$Uy z*A@1O;l|#9=947++Aw*V7WuYt{cfG8MFP0-91F-vQWKfK1X29RL%NVs75AuTTWM)X zFYlxB(i5WT9;H&`!)|vc@-jSseJ!0eIXs(ew&6<}7+8;rpSJTB|9bn2YC>m~(UkMd z>&3}qBf-vO=!5nd! z-P1syf4gdD4HzLIk59d>%fu9eC2?Ym?0i7iWcuxylBG`32arIReNf9RMKdTawVdc? z1gwRwCa|fM)GO_a17*zR>D1|yug7Dy6$G})u^#gA^n#NF@4M)Co4h%~UO!dR$@yc~ z4Kk|}y(?e6miS#WVtEViL`JfR47MhG7LwkNI&Eri)Rd{CUm z^*D<}(_u*^8sc|O`j{@wuV*nCnk5^bC3)NtlU|;pj6xdx3Hs6aul`FsGSQWV@2q7? zu73$lbfpgtJ*D2OzkGFrUwY+LbWmXPFf$*D@0~c2he=auuV@Znhz(o zFj+WKUiuDD%C=1EKr4an$@qJC`wmrz7`t~kYRzR^vD4$|WDh-Cn(QOOS-Bs z*EyWXVt0z_+RQ2jJjeOFG*P&TxYWtv>^nSu{dT{^Q_p#3{##rG7x`V*;N@FU=M3%WJH^=-QjmE3UKm@@5V#9JG}TLn2u*`4>Np#*`IP zWDJF2Sh(vKg!?=RXrA|rDi`ilYtXx3fGkSgN@6);_PBXoH!FgqCj88$W%q}sEsq19 zhrjX{zO=A*dW!Dpxz&#Xt%-;1a(PZ9a{9?7;-7b^slU_@x774pM@cEwYvIk>;*3oG zWRd!ki1fn|!CJd6|fRHgt z;c++X%5%RKr6~@;873yC1mY&wr|cBxOIz~2kPfBSo-HNp#Q{Nvp=PEe#&HwH!#C%0 zzDr|_Lq)wWhDm8?W*d5GQjO!I0bDW!GY1mMhwq7HfcAPgHTAL1L9WOw`4zKH9zpAG z4!Xu!vZi{;|Jo5u;*D*{tfnnkP3?Um$)~3u6V_a~#QH-!_eegmr34=OJfNUGcFOAn z0qdEjB4t-_(}DjaY=*~`%U7^5R^04%BRjTm862NUD#FJTm7{dA;hjd|Aeq_tNmLa_ zjcm9tGu=Qq&4LPAC19GNYS2y1+6f~-UMWXDP($zswD4!|D{AG3zD8wXBiv{85+{;G zAcjSuvg&0$Qgm1Ssh8U2@-2|%2yJG@L_qb&64m>69?EFlLVLqAJnCs>l^p}-gw+{; zVnMX2wy6lg`-WJ8r?D%LN4nK2cu8A{_s_8?Vz3*;So{>xWQ-bqPfatuHUSLDF}HZSCFp|qchu*cqWq726#-itfJ>j^L9kbUX%x0~23wzvI}#!_f2%_XmP=-FGVRh6=53 zr7UPa3+Hdp9`+2geG)*RE^&yO^FT*hHWhW-CMONXm+V*CL&?{NO^I*Nsw_g}amFLB zQDr#?73pRS!ETu?Ny=`kgS5*OPy@m!)kY8^*O>G0;y9*Jz&cQcaa!W7mS0TH2r_mC zS2;o_1_rBziLa*f>^>!Mc3?9Y&nTp=rz5@V_!CZuJZ-H`lVo;BJTi%C*O1idcyS3| z7Eenno7I(9N38Ns{i|bu{r;n0mx!E1?QYldDjSA+MwMq90F}81DzoV+_^ts+YiD7x zNNVS`FGc*o&|*CwS#CN}J|Fwe(>9ia*sN+Cnlw0fT2%-I%Zg{pES2B+5N&?w7)w9y z{=Pstw>xBasr&sHwiZzWi-Nc86)CxLuC%q**|d5KfzLrFLna{DD*d63qf;b`T}^Su zWi|wrXDkNf@I8>d(^!?|WPsCTN!3l-(7A%5xFp^v!h>$4oY-4jgYo)~ggU07-Baqw zI%`hgQ0&Y@gM~Jpio=@jgOS{8+>>c$EH`fBz!Rm$!y&203x#Y}-dv~1K{lF+84V9M z@$@V_S7(`sy^@Vy8|;}Z?N3KA!8jxRd|GWpt8^`wa|n9*%z=90XewpKa(!sj=*%3l zlfZboHVv(_MaBhl{jBBVU(H-ZKXN~ahtqrGv)yvyjYM6(W>ncAC1>G2^sSXP#LqH82ju3{_^QQP@f$ zO@Xn`KllL@1ucW+VOpfR#wH|vy6o!ZL2g-;F>ZpYxti1khXMd-l`+-BVQU&(7v9+t z1~wcyvD(6$jw92t+$B8_yOMln!)Cq@aWuQjg8pAjr+?b%iH4B4;Y3^cC46$Z)sEB| zeDV~(o^W>or;fRO!^rHXisjx2?BiAWTHEhuU!59I8`P|3!*(!+hWB-grWJl`nXmvw ze#r#^UV=Y6=Rz_UGJuvsTXaP@5`wK7X7K6p;*@b5PKT74JR@U8#|%I}dX30S*ar^V zf&f~ybx!5yY^p{}rweEWSFRt(VhLTOzgQhsTK2#EJ>c~gYCP8$&wdAIR3Hz`s@$QY zQ0*m%4XhL-n@!Ej2elNS@5IwOg-eMmb)c)xYCw`6zND0@D2cgn+Z8Th6wS)ERiMjP z20o0okS7)V_@&BP(%z2}YbP-l!N5_eRsMbU&~~ZJqoVFKe>Wa>s$(2fylWL)keOWz z*aJHs1}CG+)Yl{)SNYW~oVt=1rS1ZakxP)aEdP&V!Y*GNzc;2pEd`Pk?kJKz53e6l zPEv21Y)Y91V~nw#R?RerNw5i^;6i^MQF#kiXk9b?W>3qYM59P56<1spzYl6HlEKfD zfyIA)bG!?t1Y2RgeLh*@;4%Za z8DC~e918Tx05>gU?YEoq&jsB>#yq$7zb6$&VK_r<7T#hF;x&u_X zVeT;P>_ZZz4G+51)!eZQ`HGQYPV7$$;AH}KqGj&1*}Zt|y0)~5Phd;s9zahsMoeoT z@Yr!HC9lV6Zu9-?|C03oU?Pd^ZdfBa(4xq`LTpMFK7HU7ZGl#891FWRdxPjTO!-~A z?4`5+HQzH*00A)S{>Xu}9Ehr(Eszt}&lqnZo?{zY?4SgfrG@yJ8_21|iIi)`T%K~{ z?|pxm3UZn*pV0})MU|qDD_T^FIn=2sYQfCUZ4ybn{z6=BW<_2$Jl{*AoTx%KR%h3D zltn7u-Y`u%Gg>S*_xG6Kp97{>miKS^exyJaj=CZiC0*x^oi25SXrwR*J-bBtrG7L&h zu_sX^6uHvDO1W5h%onHGE>2x&mBxF(=dwsu4-yGtAV&oPF3=PU%^Szo4cDKISv9O;aH% z9)Z+Q7b`@o+3d9AzCllE76=Dvpj^!;OZ87_P{t%?q#Zc`q{7&=EybANI-+D;e4NJW;7!^7Pwo$U<9@J)O9I``19}ptLhCa9X7ogAxKaA}srM9l>IgaV zmNDw8L>Oo;9le&RR1zm17`Edg4k4zw9)N{hau$bUzNwro%9dZC8Dz2BfCC0#6_wgi zXT|CgK;Dn145X?kGpm7=5UqA}CN*MZIa%8*=DZfBBNrN1gb8M$o^-7jY&K18FVq0B zam%-^5+HKc(I{g@EW_$ji7s<4o(n1h`L}zNdM2xs;XNhGwe}R$PQKOBM48{ocMC@! zpU!6y2W)_76PhppWLafw0Bo!sz(H|fYMsPMu+>Sa+7Zc4lqDT#hTmoQ4yt7wd<_Y6 z_=yuMx1StUusBhCxN+K&R;!+NZk>Jkvn9|UM=4ihsx)P%K^b)r7>c{1k#kxGrKN5Q zS(=YJv%4gdHY*>qlBo?cF41C32;b9)#bt>2yLD~=`xJy?@#a*r$v9VhH8laiCIpYY z9(^joy)WER+o6FPiSgHnptXqqk_YH_-0L^lO!iO~GE*XVu#yBSOCcKi$>Q?rgJy@- zS+R2GaA<$FixJ1ZvUT>iJCn=FfW(+7i++y91dmvG*cPC>F~sCX@kc)l0Epjkb5axU|C%05@oz(D%&+7B!p2BJ# z$!52v!{c&DW?feOLjXZQfb(SYa7~wfLg29s9Sduu2tqpT0g5cy)STmCH-b!4751q5N@IaFC4Z^z!^9Cp+x&Xm_#bIZ7x~AaQD^s(AJU2+6eRbC z0(5~!f<|^Wk)%he1*yA9#}&3esxF3E9UHz0UhGyyN3#tMt%?R=RhHSnIyPkrmHr(- z$V0FZ6y5wnpO*`HgoQHRGi}Ba2B4M$O1XqN5SPCgQspEl6{^0^%7$DSSdb`N5DbuD zCQ8J@kam!9;a=VNWLX05AusPogON(Ex-b8vt~2oZd{3zP_7e9VwvbQqp(Q#S_x<8f^goIjUwKcdG)AC~FO@giIiZ1bweeun9) z%e>seI1>7GlD^L(O7*!s`swrSx;_zF{m|P!5Lb3Qfbi|R-qlMx$y87;)_tO zRos4-uinf3J|Xo__hZ%xzS@Q@>096HS@p|B^Q>m?Xo1rUU=ORq+aIEuSEGkIydMi` zS{821Gd=o0+E;Ct)s7Fle>18M{Sxp`GU)Opc-PVA6^HYb!Q%qTZoQEEgb*;O=_yrw@_JjX&+;wNa!$v>1Hvva#OZcQTtD0wraUdz4!}b8nzC_ zs3?$j32taIjp!`^xGKf#W=G!4GeEoVLGTSKCZj7JlT=GW9r zp$NmW&9yOGH9wyBB7H9LLxd#M3VuJ#NCr(y8p@w2K}VtQbrHym>A2A8`x;NyFZ%jM zfxI&7(v%tKja`15q)~iZbmkg}#7Hv}2r&XVb)po+3Bn#%W1OF!~x~iIHpJ6Eb$s29B+~K<}7{WnDUU~kWl*}NA;z8|@7(bmulqUBuOsQg%WlZ1OrONl2f;>n5+pW{Ahdf4kyz zVIzUOt+){SPl^#rgl&6$dA!_-Q{Yif-pv`03l%o)Sd<$pJsDx_jDI72rJvQ&eji|l z1s;#%E~q))LfzUHep;7Oh-)J4)~rmK1WF_T2aNxv}lM2SHxxhZm$i7vByN-}yJv zupmO8rn083P&gN8U-w-- zu^B|@ig{}?|5KlE#j4k~`vt#8=9CoLqUs@#zH_r?$Y;C%X+}kXJ9pRsdS4X_A(zs2 zg%eqI#oTS(+fCCv!zGseXlWC7${d=ZsX=GkxyJ=YP$vLqXtxms>>Ru(@XsKKp9$=F zd+Sj}>&k|E&GX_H(x?5ltMO8HXR0)wBbCH%7m=DCwVz+4jNa6WdrlavN(B_LQ;4}E z7HBr|)t-Kqn!%X>d?-~C|EW_V*SOQUzuPMUsZ6;>jlBqvYsDUJ$&z-sLPDKRm{4Nu zqU}?Us!)#zV#x+7r939zaxax2mVIAkNgZCnvtNUGn?(J2`gi`6mJLz1C zVC?`RnCS0S?*0K7j2SYFb5&;@kMcy$;7ekvX=GVCry(f}Q@-k+L+78uFx5kS!YnO` z0KeDWB>kQ8#x+N^sa4nIvCnbG3N->srv*uRu_&YxO(KgX^&u{E!8uZyUTEP3E zX;lO6O?SCqeF)iQp^HA?J!_keSiBmP#QGxu8X#i?m~w+QC_YD)=hK(jH&x;LK3!Dk*!bGEU#po% zFSM#XdzoCis1&BYF8BJGk^7$Pt0H{K7|Ogw$I7aGfEHU z1YiaPqlS^}86#A)Y(h)w`*R9lexRdiqNw4wD9Me=O#TTI-@NYgIHt){1wRS1@~EuY z?S2=mp2p`xG2*L=_lVx~EYs$Y3Z0%qE|6K95G_Q{gR3;!HkX!wbk5?>7=}cPjsZP3U=CEqx{@ zMHj$O7syNFUF1U=Y-n=wFT+%b2NG7Hn|BByAW~h|MRf^m)lsIOs~v~+;qxHEItyPk zt_-Y_h=Lzlwl(tmL|GQ%kdKncp9exWHcFnNq$x3#7*@v-u~PB+QF^U{(<^vxrx&M9 zyWOy4`g%}s!-5l-)Il$5HJmEjC<)&t(hF@twX^K(>hwR9#_YB5V=!7k>|~O)S(pR) zeZ#5F68oB?N(hFgM-@|F+o$uhrzv>K4V$$P>yZF#?3N&c$`B%*(ytaQ`M8F7q*zp$ z0ni@-M7qv>XfW~J0Z0N1*Pp^>-c;yz8CTP^hw_iyu-SJxPkt~2$=N#9QFxF^bVgx{ zmr5MOp;X2iaZM>zM#RY6sO}pz{TA?IP)2^y;gGa}>J@e^2Zc>`wY1200i{HoRPVW; zf8|B3@BlcjbB}XZgqs=`&F~s0UN;SUTi^uhq;>F~gOVo&$yEIBrWFLa%M=+(U{HJR zg*1MRta+Yg2;H>>Ms^u*E&L+&KF1dQPVAJ)cP9{(1a@*D5dP9@N>rwi+r@=;L<`T3 z#5a$EfIVUoOM?>b_HL)`@%kv~Sh4Y=n{qHZCfz9tBm(XS=oNi1+*iM2r%x+czo{`i zf{{1C#bs-$4k%qB;}boZJ;CPz7i+ z?ckNr3jcPQL_)(lFgl<7i(|-;r0AGa{EsN z|DcG4iu9XKGeSyOZ`Ne6kj(@9uC$$L5;O0fhLSd2(8-Xz=}I-hx68QguU}VuL-8Fv9v@RXh9XD1SvXL|b zO?@<7@G>&Hsi%tf!Z_H@-$WRfe=}RRjD_ku~?|hlCmg7G*LT7y)yF70drlct_ zD@^8E>@%pIkcr2kO39iG_+^tuKeQd#F>!yq7D-%x^U&(lh5C_gX@$De#JL53qIUFmM4nHz-fs1W zLUCufhu63;7s#Iqo|2=E*ef`m8Rt;E#9K%7#qCG!!}xKDEOp~Jev-c3DsOyovxn1M zcbV5Mkc=Ye9<$BZk9GSn$Ezd6k5hplj2L^j!Qb2JcV}{{X{U%h1)f zTt#p>zRBPF2!=I6mec0^=hcKk$alXr`RtIL)uD6l3ujdC3bQ;o%{9h}5DcwFDAX9%e>6e_Vz`_&BoD*) zM}+X|{^b8TKQt!ujR{Z_lYLr238sPXJhQIhsfKmZ zLk`a#VKi;u4P6l5`lTE~2!z$HF2)4WEcmDGF%z#g^@|lHtQc zZ9}q8ki%S;<`!J|R?2$o%VW6Vhs`5^DopDEGCmYVE{SvM!GILOMwj9Uz}fV~GZHWx z#pb+2bM6ZSUVOQHGMwGRF&Bcl95R5u-eAB>cZ&0aE9*d(A7^i>WPprut$OkM%v>ql zt(({pbVfe>{p~_9c9#JWsqB=)yxBK0E+JD$6!8DV;A(_7w!#=)*U=&ktJaT37iUly z!rTxZ@Oy5t`K>HND8KzmRo8I{oQ|m?%FQyyAr~7-g1do35{GVn6E>(|V1!9cJAhkf zJiWT+e7?27=z%eom7FADTgG(l4nVDPt0FPYTb6r;(@-93wi;Scri-L$n^#L#G?p40 zWy#`ZsnRApPUC{3fcVB5fn@1E4+mkY$3vL##B=Q5=_HzbpmYcQVZQc6ha*CbRFA_7 zsHNFI{y+B4`YEn=Tk{EYaA_d8HSSJuYb3!nxO)h}gIj1kxI=K406`L50yNTSa82;w z?mqi_?m07c@B9T*HNSS%uHN<9UTZy{^}IW1v>42V1WnSX&luezWrOG)(O*#YQa5T3 zkZ;KwLc0bNcf=y1x54bn#`H__GYy_mnD~rp;H2i}amn2xYQ!z^{#Va5r4o`m*Ykuk zxy0VWT1so`?%_nNG$UJi5S))fQo*~jv+3eR(qs+g$xKO2rp3sgz$c$86}7Cccs=c7 zSTwYOhOlXw*CH&FE|o*xmiusOHoaP{L)i;G(tH>GzCamZ55H%l@^O{CABdx00oh05{ANC`z4 zl4FFY%N;I=ramasoR$>@<-|6$i^m1E!Vqg8U*;hIv zpSc`j8PTR0d*}~}q0Hw6$NqIwaHd$Ee;YhIKV@jBuXf+P0gj^&2{OD!xeehO@;K$q zBM(O{oZl)&fZ-Un)0tdy>KY)LQFBBX4~Mfc zn`2TlPL;c>fc-cBV-yDKaL`cuvy+ z#N&7IpBkr>1KP21R}?DLb~k|@tFS>N@xXS4DjO9VH~2PJUPqU4QEXL74yPPBO{?~VVLJenkql~y|Z|~6|?xXwVd~am?5HFv1p|4 zptxwvE^*m`DRMWNDD=xJf2vjm!r@DEBtML4W@?uF3q%@=CW6_5UaSj#P^G6wCqG7= z`WA$D#lO_7o}*!&x6fP29N=#}-+$KhkuAc5yWd-;;??k??pl8XU zz+c9&VdMDhi9M6>zP%bW&ScP~A~em{g_(8MKb@TBYs~O2|B|Ow!eg=@Eqo6J6K=Sq z07+q0V&4?&tDI$A-HZPWnQ&UeCQa}588*Wl*=4mBtIg~eVbUfF|pG6qlli|HA2D@sH`->#jrMdi2Y)NKR(#=f_QYQwU&tO zH8XZ8Sl%M#B$dL|>PM3OeM^VEPG7MA#vpc`2h*BAvijz?1HD_gN~P+v`abg)@YE-% zpu#8qY@u85>dNIBA%{*cJHss#5ZwD~J9h`h${P0ryW!VGPY)5*O-i^8RM% z9u3itylM2W_%mf6NoCsW0?ui_7CO~8NZh!x2XAl-y^^bmHtlyvDL0cZ3L$sm4f2!e zPSQ6F-iRNUMe`KU1U8T5&O|fNCM(`eRR7`jjXw1qX&dUpN5S&lZjQ9Anf_+pp9$|@ zCf^O%rdvN_oqv!OZNfZLUG2bO{Hh=a77k-^87LWU{j?=Ps(*QvWzF+~hv47AvNCuj zGbkq}euO{$nj>jFDW$7S`U72eBj@xvJ|cF+!?0oFI8lkyWh=`38Jlg%gcVxBkmGJ5 zV&gq;W`qs3^&fcIOYLpHa01EHZmP`kx;(e^-uDxS{rt@;DpUC=T#3;(^BB;N2z`h< z_-D;#0Z^-GeU4)3-$uxm@gtHPUYdV3SP{o3J02_rLjS_#I}es!kO~U<4yq@w6HoCX zR;4mv_l2SxRJ6&_H%YdsB2jRwP77q1y6jjgVkVkGfGwku;XH`2nA_%P8 zukXU{LN~OuXen5!7=m4Y118mW;b$b+#mIOGC^kY=7NO%f< zTZVl+N&AOlU>D&k7WpvlykQIxOM0}isKS~}iis+xu6$-$4gJ+ZTumGW3E?KWN{@xO ziv2-d!d->ybvV*RP@(@vCbjzftOW&m0BxE0e99TWUwQ9zGhGHRn;aT`GO2w1fc>MA zO_-iEMo`FlA&pT6Y`Lpf;iglz`D-L3U!Y47@s)E5{gADgRfk!FA*6TOyFfwl)qea9 zQK5(>E7`mMcmX_wGV+Yi^_DTxP4}d-oN&5Ex2+@XhE|5am-3!5)k6wG_>flKTnppu z(efCwKZMK%h<+xuq8AFMxO0B649W2~p(%BEXZ@B%QC$f7wJP!%$SlO|{=RsofR%`LWb(N< z`sPtA=p8dmp4uOa!bLwzv^pDYYb2Qx3ly^cT-=U{oWn5=+P2L%!!W=!%SYnd?_yMF zg|IU!jC#yV_O%GrW)ylAI+l*1sz)QvsQ0St1?{tp3$2uEH}hLedFwhiM3EZQm1NX} zbl=x2`;J(N;J|hH?h#)SarOd=-Tw&%K{KXteBX0XdS|Q)ivawlD4I|qrK^_@ub$Z- zDg}<6wz)od>on)$9U`-rFX`%O2A_b8a1VJvUpdE2856Ppi7R-UZ31dog+6JMLpS_U z@);32yn97JfLL9Y@^8cdjrQj=UJ=e^br5pb%~r0dZ6exy$VVT$GPq!eMCWiAMS-Q6 zuq?5Z(;453>lR*p0aDm@NnyMu!zU5Rf;#c+7c%up@5PkeQ%qzN$IBA9O&gV(7l@`U zNT%)hrrwhPL`Bupjx1?LmQxbTdTCysjNWJYfoD+5QRx!@m4Xu52xb_V`K4U4`QVzw zwlQHa6T&aBCk)$7Hc9{@+w?o%?IX`i-~x%TYf*h30ONoysgM?6`~~+~L`K`VN=6!dG!bI= z@eY+C@%*t&-njJ7tuyiHJV8{=zc#3Ofy~3Fq!eo? zU>sNYSA?=~f(7L}S!y9u{3CV2jikMy%bH^&(E6pbF+3(1ioAeoy70nOo@q?R`B3eU zqf&F9u7t?1x%1&ocVj;U3yyEG7W&A;R7jkIz8se80c#qgA_cXse@1#l6FM6{=G|)E zFnX7p*4&9=2E8;IbdI$$klQApW^8bKGzvWA`%ArHaGC>gnWnw|;UrEOh)=1BurW|T3RVtF^tD0(*pOFy>A(h3JzSsU}g)dR2Bgdpj zL`-RP=1Y2mnLztn{Ih@{0V%e|y+%mw{mEixh(@AO*@$^skCw&<5mFf% zFZ=SnU!ewXq!aDm81_Q&cc3IaL-~0k5{Te1Vfa9?!Co|!)c!?kG&fYX)*6RofE=lu zu37fi1c(J-j$edIxOM0u)Vs?inZILe1cUP_3jKIrA>(q-u-?X_1BDycLp@r*MYCr- zw5?wocW>&j6~O-4VAJQ&Z_b2wPp*$~@AFpy+6+M@>K;(r}uwzIYX<{M~9118=$K2aG&t5lGf zClxDkvq2y@_w4yb$J&>P(&IBQy-k-$VwFrqQ+WGzy3mv8Jg@RnQp(BVJs&bn$P{h7 z=pklxEw$0f=0n>T^I5Y)JN#bmx$ZoSHB=(6zMhNgTTKo=pi4Y=NfN8YimY=rmqYR( z77zYbx2xgbeX{I!paH}Rs=ZwlR7dQV1hlmn`G*ka0y-w;L|S-5CPd^vg&EvCrN8*8 zoGW)H@kla^1dDyxKH^U>=GWXon2%qBW+!fTp2SAH2r%VEd{5fu{8XFZwM0c2C;={j z_E~(;TAfGDU2M?~RY``Cjy$>8kzM-F2%quq{ow6_ELQ*_5b>9sNLlw9jmxAwod8QO z>P5Gue5KhPF2NZ^J?(!r@U}+t1XmL=#G1hk>y;?k9oSev+57a#O}tD}YxRrS^UV$= zcZc1{Qsp~5PSlLQCMs7Hp>cNm%U}og4I@dXNadN*h3cf`vD$f~AuES@f&U61fQ0Eu z6lt5c$()xYSMm5$&mz{tzl5sSpRKG(^}@f0_PkC^8Sx+wUeq=5E}nFF7aX*-FhE0P zoZ*#RycY#1Puu+Y{s=S+g54bVVm!Q+UhU;)dtrwGTKifuxrNiV|EC{kh!Y8m9e4Z} zL5Y?{-Ya&nEPEJG)5|`aLObXkG=E`TUDa4WEY>#E`LB6ZrJV z>Bpj1&AIm)yazL&qp$?k;z>0!{&!UQ(UeZ=5UHCn42N>pmF+s|A6e8m7FBGcZKWLk z)^dXqG6pr~&A)4z;BYY&-^y|`dWU_(+f6^7B!jZiK4d48AaiRC;`u#oCw=F*|T@&n$K*gonSLD4gTJG>C zkYB?VTv~DcY~-~X8ap-NRQDv9OalGDOkYLypV9x{Ry%r(c=B>!#JA@PbbQwn_=)lo z|ERiuxp5L?`wj7F%IU|=RW0g*`P_A6KI6f|WG8od|3}*mR**GLkQE5ebK-1Nbj*6& z^b3>h5?wvmiw2!$W(cA{&*-x9GYDPl)6MCHF&VUy3+B=Ge>jtW!}IKuXh6YaQVu$( zY5l=8k;!+#t`t$GQHcKqElJjbDN@Y{-vZ>KkoVsG{{oqR@&q3d$#JPg75FA zE4tLqu9gGS(mU<|u#F8cps_o?t&;xN+4f#0?+?hOI|TjoV%8K>@exqfH8_KiIP$V-yP!9s6fslmqz2hdllG%M+V z8ll<%NWmdJal(H|^qZW`y9EM%ne@j}THkD@c|HjM3}bQF^MmEHwKQJyTO*y~ADR34>utkgT1^Jj{hDecF|)0(GTuTFs(=GS@0ICq))zp}QUl**iq1$Bp9p7? zv{K!n@jX!GWP{zJ4JZ!z5 z)-^>?`R=dI)K@@)yW?l=zqRWO4h{Woye&#n9ek&L1qk2C02`^?FlP}J?Tn1v5eJ-( z_ygCq{ZxBCf=++Lns-Vv6EDBJ@6FV8W3V*^eywWwawoyak#0jLf&iXo|N5SbAQ=)V4Y%C{{mnn@M-R5($K}dsocAlzfSXW8mW)5z8nLM#O=)g`&wVAJg!AZI z#=KhVB!9md%{S_76?a?RcB(iJ1ltoj#s&tgq6goT-oAY+#Cy|)N+ovD}Z4qLxzi0J6T2Kv`pk2<2RGSY;&&WdReK;!z@KQY`)oJma}QLnA>_y@&K3sV;AAkd$s)1Fz$lna zJ=C$+b>m%tEydwNoY-l^SE?(6(cJVD+gyL0zoS(yS5{0e8BGXw^q~EH*M)Xo|553A zz$ECjSM3##Ffm1YAw}EdE%tEVjUDu>I(ALPoMF>7CP-GnTqQUsU>GFBYfB*lPrc3 zIcOgMNz>Ze+LBcOp+^4{U=iV0z$DE8kWnK%FkF{5A!5I#2M4y9!2MZp*-z^@9txy6 z;1Ym2<6Y za6C|#j;(0#hoS(yDTJdQ3bBVpNP4O7z}3h?Q^WZ)?ty&-TN{1147^2FWrTx0J*Wft zt*O^RpFZMeUwUWR@kLwoHHc8QGq-)EIJOo4<1wyY=f9SiWw|4 z+HoHJ4uFZ>J*v4rtW&oLu&*;F@Q55B+@zZ#C-b^WgD6%Iw?#{L%Pe zdUw|K`0i4lf~0c6Hu}K9n$uG!4OW0jYZB5MNAw#ywKJp{xkgTs(r`(<47rxZ-=Qby zFdh>-i(c&YbVyPWh$J3LhPXb9bP;Q&hoe(AutWjr#aqa8`op>mALNl-$>jv7SY%B{ zES30z_0lU$3$2bdl}0ChJlU59FAcd?H7Gm9GZ_?L5fQ)kdVIWd+Ge5SrG`gn zyjmiaZ-On-^8}s8DA%+NC*nCTxwAy6iCcxRQ2hzGb0Btl<_#U7#Y91+{HR_d44f>U zOG?O+?)X!XEJ??16Xz;OEDf{!PN^i7V8R<}|EU;(H5`QxpocF$(kuZ z3Wcp}ga6@bNmfvhB zZgtm~fe>2s@aE68s?OfbxV2F4?~6(ErvY7qu>qG!>tfD>kO23irI~DOEo;B~2YK(S z#ZIf!bMJ{-W6YhZ!@}^xx*&co6EPF~0>1M&m)PyBWv+3)wT!DU#5SV7c$vz-Na;>T zdM!gx;z_0B);(Hkro}FE?|gUdHYGQ$mE_CLZVC6+>B3geW@!R}b!Ee3i(GAGorUkb z)d(@>$!_3LO0}Pq=G^T}V*j?azS$U9GJqQxoz~f>mNZUat7!YXu}?93Q{A1~alCSX z*iETgJkmAiZsm>lO_C{t?wd9zQ9d5T1PU2Bsf1&GA8WvMwoNCM{XRT1?jHSk&F$&hNJ8IYyihAWZiran}b6;`=?$7%EL{x_`_)Tz|eu#P`wRGZ@ z8%rrVDR0o5m;;jX$L%W7))k4olxRxb|M?&e4q(>eQZqm0kxP67a8B@B_SbVO!A4Ho}+89 zM=;?^^;qwAOfRnAhXDpJUfFk$0@c^K7`CF0_B^y}`0F)^&%oEQs zn7Ou%4TaECc`ZJ~r< zP?~+4&IfrJqa#weD?~edgGRnYU%6g9oEy3!u_7`--R&u4I_TU990XAzROjmUl*^4Z zc~8eNYYQ5JSF}DTx6%%?5_-hT8oBB*4JJTYW##Z%Bp)Ki6)!gMC&%}2XVen-NJSG2 zd#%c`4ys; zV|@3Exq`sW9Z}Tsua{Le%eVG^7N>4uRptafdK1Hfe+c~cw!LGYBqA|`NcXSJuaNoz zaq90o0w>P8A=Gv8W)yhkX@Fek!LR&ay7q0xt{sFIs*j*Wpz4KptUqN#B{>psHks__ zs5|3nMBHpoX*b!SxrY5J#+P7bZe~7(25sXfW#Fh~1+FzN%aAA$|)^ zOtCc-lE|xmDB|>6C2d~!VIn}<_FnZ}?Z6=L=;Ug`vJtWr-HH3O7pgYsSggg)^tDXT4X2o}>uZ}*s9r_vndALZVB+)3IcNcsk zBCVNaJiRtt7VRd@`MZ|Z$%IHqli0!W1zdhMPEnWLXNCb5P32@^1i3haaWvnQ-UWRK zL!@!-!1jQN4Hdw>Ax81>R<$4h@$>P!7l)9o7|qBOk`V{@snq+a!!Io z&!Lts6^s4ly&x3MO-Zss0ZBzzad7xvDEP|9Z7y;&hCTpni?Bl5<$a-Q}O7cih3GfSTG+b6mYf{6qla#7E>Jx;3pjD}FW{!_pm78=BcA|@VbCFuaApHvW#eb4q;`}OcCE|fV%T)T z5!Gzwk$Ao$6g{!UYI))VnuU7WI@K`!RAdkuirY_!AN93;58rD^&2~vm+*Khiqk)Er)dc%*5O7hk#&6|r6C1e7K*~Es0TMi~^1PD{imrtgnY>vA9W-H+f2jIX$Of?)pHRs9_qB+$mG%V z!Qo7aMg++t5W6dCjb);t6@KD!3vhEzbn-2U>`U!rZgzAux{GK45m-KuT`5Ky|3=1f5=mQU-vojUrm8@(VYG5Sgb6b`Gx~?Kd#Sx zRDej{IdxVWxekw>u);uiY$p2FDf>UB0d>YxFhj(?W9e#8H}?BM=YzOWIfTjmQB+WzG*HF1^HgKqC&&ya~Z zjo~vZd?#wr`P3XI1T%P>{*9>RhxLPYtD-%_V&^@dLewt8_73^EeRh@s!N>o+{Q@mD>4;9_J>`f)ko|tct0Sx z)Z%hEUU$9>6?pl$YQ`=_m6b$OdDr$ei+|fGcOIO#(RPYwCO9b#hq7+0T}Kl{2SgNq z*%N#NX~u57{E8AB(pC=%WVUQ0zP>y*hI%xNYyYXuLS24Y zsYc&#@hjGSfbpSA^Yuepp%8WQ!*T0#F2WAXc*V652!XiIxw92=98O0*ILNuK8Uig+ z`6H(n=XK;FcBCau_4Cy|r_D-IORbK1sC0zwy5UynbCula7x%(UA2E~Pcckb&{Y*UT zH|({<4TG*44qsf%f}?1BaJ?qpRL8|NuM44M({TS#XxLi$Mz?JBXX`vS036=uelbI~ zfE}>tFDYh+Ng#Vj`4Pvtg!AL`kDLq7EamYQ+ZW5eas9?e8vG$M@QWv@J zSv-dD!S~6HvcgzJAE6`i9JO=gw$q=&l+en(dgb-U$0Er#re-GZ@Y4=6NU(i)^~eJ# zbtS^{*%*fxk9=3&Se)eJ_UH?l(J%JZJJB@5o{>{q!XAWkxwmwAT({A(BU?N%T2W7; zfEohS2eoNN-VuINLdYu<&W98zZd=@%N2Q@Rmg>Y`OWYODd!+;>%KxSE4bReGq3-m7gcKliP$#s$m$oWF#+A>;u_^4+>mb^^+H)150> zv3VF4dE6T#oo4NXh`vfo7M(mSU5Qvn_t1G!RakL)P|Pl;X;?FxceaT2PBAc+P^Ws4 z4(<*K=V!4#=UF(vD6NSTaw<~^thke zvZQZDysaeG?Z{8O1P*Chtde{>lEOL{_>$LR5t91N}-pkI1Y3h@PKX;5L`i0IFZkRf7;eE2EHznc6`TGMW zla^$)?8oI z9|uvNNW9b84)MR2YE+LhZi^1HXV+o;jnXlckrmt-o2)%n7UINs;bwYs)QWSw zke-8CtlN`=fZk<6_x}plsga^VHuNH=>zqY_&7n?B(X9G!<=@ES@3tH?EGANos;n+`t!Cf^Y(V0j$l%PHC)cza;!T7gxUtH>)((p#M&xnE?uRPo9t=Go6rpI>|G3 z7mqyp>cBt>-5_~My?grfaYM+2klNkkI3PE+vf1A+{y`x12cE11X!mjR@gyIYYHwCc zTuZEfmzHo+<21d`xc4q~XE1Vh7F;wEM@qrQ_RVO&rq%SCR+V;YqH!)jAj{)SsT!*Y z_=jWEiyhA>HqTy}qgxe6Qu>H*ftQBe5>M;LN>p)ImcvD|ND8F#5lk&ye%JMq!yJ=% zlxnIT5*Jk3MMS#s<&cOVgInH;yXNv}^s?_cFnLvmsqr_;9Nu!C2gmIGTBLM2J!OkkS49lpH%)vX0h6g;GS411JySjF4t}Za& zD5UqMWN&I6)tmGa7QP4ygLneAsi!>hRYo5Jduy)&wmWKz0OsrTY0xYuqt>s_& zGD(Dy`K2|I*A$vH5@RkCVIM5B!2{`#Fm$a7RKX;Zca?hgeXD=fwOl89NH{J8NtE18 zBhxUi-u!l3ySx8PeXyzbI(f+z4@ z8qI-=q)#uuA#D3D^Mk@7GmVloqiW`Qr7QDa^RGEhYLfZ&)~T~OiMe*59K z_s02$fZEu6(3VtgYRpYA=nWglGvS5SUW~aP`@Uqy+$q9BmNUo9(_PZUURl##*;6Z? zySdVBI@+z+iN$7f>*}aIBz($4qNI=bf(!AJzi(tZ+-)FT{tI}o>OT60UTLKB-}vbH z*jvY2S!!R-(brSmuBS0VmE9?Kh5$>1wFhhi`}wrbkcv!~dy&L;SnTd^=)eUV=BSWNOBf^S?|F zp`!}MNd%Wbe4|=65HN)q6m1DU(ZEHiT3=N2(=rBi`uo!~$($%Ly_3|I;&;Iip*;=U z&qlAIlp_o?{G9&jIl2-ObGkggv^jrWuoRj!ORD-OsR})9ekAQr2FZ2kuuq@BPyqtj z=L8|3=a!!=+-S-+rJujgo1;a0DUBAnuQYw;xAhmxUzE>n>yIc`+ua{w?W2*UjGV)r z&eF8W-&>Mo0*ur04xmjL%n5n^cAUONZV%f1nAEVZI}!pIbDZgBdT|G!iRBX+)n{5d zqA*IbyXJ&B6ui5ae#=F$^^aKPD$HDiBOnxw2rO`U(Y%Cz>sW2(6@8$X6D@`&r*GEB zkCe6PUmSvaOn zLR!;c9V~C$#Td3#)4LMkOeqTbP<|`JnbQgxZdz3%bI)Llva>a28ZJ`XNStnC7O+U_ z3`poR$10OMGJsoeeNv`CiUdv(Ho#tB;aJV;rYEHx|zp>W`2c3poN%3bixE-*Yh<|62% zelEZTO+t(7ODb~muT={BQ#A3bfSPc#paRu1+r zN%&#@4X7trijutUsv-#54`~Vc^&~Y2Eb8Eg;@Y&`-pwbgjf%>ebMq5_Hkn;w4_EI^ zl0`_LOW&3=C3p2IrRG|ekkKD+LUMS4$ZJ1q>!dUjKaL;J@4m^Dc7Ru>`|bJ8!j&ao z6bHSBUv$t~?Z*Uc3qkjctMXHSd{xFISS8ha|3;7SJ+y{Dex-g<h5e0O=HR^1m=IAK?p zbRUc&aJ$-k_b_+jtSwMR1qyfXWtd$q4B>CW8PD+zj^ZS>bc#GBCNRf30;?U2b=!Y z?9+;pM{{Cg&Wlmu$i+)BSX|bd^TZ#`%A?%E+Kjx5-#FdTg4fKgxMn!dzrdJVDUzjP zpyEf4viQ~{c|W_5qAJ;6Bt#0zTd5C!6(Z^nMN%X>uvs$YX*SqcD@r4j9YiW1W1P~6 zW>F)MAA!g~%Og^-T1`Dd_G3?IqKlbrqIaymmf>Ij>g3TkC!lr_<2EW-DnOGm>w!NT zWId63q;HK{TbcK+gsfp8m8gZdu()H>iVQYrkw#$9dOv65mf7CQP_I)!>NBXbD81S@ z&4JB17&9JLV9*ZIaH{HU1Qmsyh*<`tYxnAC{vMOMl=&T8G+vbH^TFl1))%y(3D!4G zs!G$Zcst0N+%=Cpq-IvteE>qTB(JZPgPAgrVTQOF;zfyiA6_hA6pXw+JLI>i@kl|( z&SiNSQs0HKwWG{68!w^*ZuM5H6&}zN_s%8dv)N$f4Ay{#jK>oi?0tF@O_ER*Qp$$> zn>k_ygFJm??le=|f&?=I$ZpMYtU|vU-=tsvXuTYksqd$H7+b43@+8ZCtkv=)3Jf*0 zy_!hMFM$|5TKii6G+oOM^wra~&?t*n>5s+H&2i1E^EH@jA%x3~v7`4JU+*b*nL1Tn zg^u()k<6s5w{*|>JQ!c?(Ly3B1^?(1e!qwJi^pc?J90M= zG0tQ#QN{n>^>~pm?=klVk49o69b70nXL4kdw$%4z#SC=V$L zyYGKiH;VL$Hf&GMXM>PgmL-Na1QhW5=@ymi6+k*MW^aEO8%+_?EJp>~Zq>pfq!FCi zBG!4V5tV*DvauG-hPLh}Aqs0=F9iB%)Sw#yz)s)#${G8jDJwu)O3j^d z4;Cl(aq;o$$3MnOHuPBK`hJ+pZkI(U0B6tKpJx@p-Ckq=;MIF!DsR=`tk*i9Zq2yb zy}P<6IxS0=qkiW7VGN2u{Ngc_o19zD+LtpeBio_^@bT+jQb|fPh>n8 zW?-)ojBz5?h|;iC5NW-?Upn95e!z^n*_xumy|4GB zj2)*fM1j@2N2Vwheb#~jtGSoJ=l{3(rlC!6df$jsy%H#}PehJa{R4QUBkoFN zsBQqImRSwV7Rj5ix;WS;z(o~-bg&o-8t6#lJr&d-E5{%8j{27>g*Ce=3^I#6VD>>u?tmi2R&D$#Jtk-%j$RC z5Py>3eW6EQL1Ge_ErmeV2;QMmEQb@(wE%_`k(aU}!^z_>Ux=%{`)*lM-^P9ND#F&8 zyvwYeur^x!D!!?X`vp70*ZC8B;@x%`)e-7vh7{Ehfq1X8s>VC@k6mYN`kk-5&h<)R z@2VvSNeo)G!8C%j)XafT{;S0$ZE#7mz#m7pmsNprPS(}02b0fBP+bECJOmv?&CZJ- zwej?dsyO@oHE!A2qp>2;l3O;;h!HH&yb|ZA%~D7A+!R+c}fbhk`KY zTe4HMNyl{(VDHjBHPaN92bq2M!WQvO9z!p(st>4?&2hw4% zG0MBPP5Jy>f{$$Y)0bt2?rsu9B^6~IPq7Q-`<;&h&pTOPXDgE!(ZeMcZw-U?j;>k6 z3Kew3J#O8J<^SttK(|}uu z=;?s5$GPpixf&0s1x-f#(35tLU}TZ{2(v#_#uq_D#(tGv4Oz9WucoF90}C_F#A zG4Z5WC)68yEy$9l;4VQl%C00~nP&D7w(DCy8>bBng=G3ad(NJVgCKoZ=7GmG}n;r4d3cX&2)TujYQ?Ok>NaY za1|!{bSqE7XeIQm^TuP&bC9Xqv$M`Ob;Hbx$NzYFEHsHTlln6AQ~_}W=l(E_k_nlX z_CpQB{MKst4LWn0kNw(OPwal<461W+F6a?d)1oOM_^_I*2&!K=Huw345w&9{*{K%kejRgHcejx#H3~$)UzDSGM9dwj z~lBvz@=)8$DAnG3a^7^uo6E*h)#cXDtamrcD(Vdzd=TXTVC(>9*b~!QrXqa?X z+T~xuSp`h2l{4hnOE%&pPuYX-_kWN&nd!1(R0fPiV=gdM(A1Gg253F5@g+}6=PPfQ zZs#gPN`s0@aW9Me*TL)eVg}Z{6OtKi7PJnk`zWz0-;3JEXm@V|u_qs;A*bS`$ zT#%O)IBc?dn)yiaSa*z|$%l+4x0%o@Lh54UyFQ3GD(m^fYmScz#%vYmT~JlfQT-jG zu=rHfS2V!XzPB&han@NBH_4saDLdM>1Xk0L$sGMEm?JPln_q_M8yTphh^j2eCQoQ1 z!KLda$Q$mi=V}s~uB1-Jw7}MEb*zs?G2rm)%;d{rE~DE6uCN29*vUO30^eTlqr<=* zr$>jH!!OVO&Jd@z7tA`Jxm=qphS6>KDlyUTs)H3k4B$o-WqDdGy?1fjag}Ohf{sS< z4w@qI<5H)%v)C4>>CKfvJV_Eb9`c#nU)VX*AO}KyWuYcN?Z>P+eXuvJktM9TCN>%#;Xw&K zX|lULMVdv{8hNqdbGSYU2?_wy-3VW*H>xk`GU;C0ez~jT#iPZgPBAp{UY7Dk+D2xP zjPCg2+b7gqT-thkS)>S$vK5l^gI(6``*e?9q({Zu**N-o;PNhIi2S}wO{!sHOFSO3MTqn9OSC(EJH z)hH(zkMSFlX6^ZZPMuQ3O&Nph)B1vNA{TB`K!Iz&LUm5Hmvk|S64L12y~XD}GS znCdD1jJy_;#5`1V<**dU9yg=>2)BJVZ}O&lAH{^^p7CA}SNxt)lg7#~`tAU@#mPjkGPZ$MJtFQy49i}zy{}!s_u|}THlC0#X&?5 zOQY4?2jZmFa>#ae^?Dr#;)Ga(PO?Wi&_2E7yre&|#|5w$c7u3uu)9gJO~j+YEpWEd z0l`PLq>C56hj0DMaeKHvnWF}!NC<{}$dxK%^GXX*_Ny6zLqzVaw$-5Yv1fZS?+nQK zeYg$m|E*?LeoB=VKoby~lya)E(=@YkyU(=gLYz)$51eEpi%oZlNs)XkVqa^8Mt; z)WT;)^d>8()cGnML!3%bdP-c9ru@D^f#&-Pjxe7mGm#;|>k)gQnp?lMw(7DZ=Khl; zww4hTupLeUy@naU>u%n{?4``?DY^Hq2+bWcmwaVrV|KMzYt-0bj2Ed1C#}iJPG$rj#I8lFBX{6Q5MyBoQL)f7UkeR}t3T!}QlrYV z68wJd&EGc$bny+o$5sLOItJKG!c}baSIWO&KKTFrz&4NBsVq=(!`Eifpc+@X@Vl53 z2lqBrv0;ng{m$G28Jrg81DT0(7nURFEu3`ce?1UlgJBQVkjyu(T5v)nwus8^AAh{9 zZXRGTPAuychi`=YCaZ;sq+)bi@_(M}->KUoUzy8PZiS-?j=Ww>Aul#+;qk_2lQA)X z8D@O}5-a;|_HBP(UW4GvA?PsU)8zktUPsVtBzWLDErdW)RE4243aRRTdb269joyEJ z=VS=^i?ZHHhvbL)zu)M8{JY^e`KvJM$b8HZ(N5T6s_XFHk73ssivheIQdUtvpa1dd ze;;yRtP`)NIrj3)|M{W+pSQB>1k!~bp21(vx&OECg5X0KUn(qc1$N9Z}^XmT zgwR@~uKyAG{m+*IiO~sC(EsIlZ{R||Vo7*m0!h^WdA$EWo{UEuH2Zv)?A`jM$i^U; zLr``v!+qXz(Hf2O|A-YEx`=jy*S8%BD1C1C3bIUra?v?Jz!;9FmN)$u-y2F~90sVy zEYhSNrg^T{Bea=c#PC^kQ=UXk#{irj%TKw)@I%G6yA|5ifx^L)0;b9%9-ZNKyx{>~ zucXAMYdS{3cPM#5c8D>oxLJ$;Pst<8pCQ|D!FK)o8m<=ON}Kz-9iF6`G}6`&?i+)$ zCj+4d-mGZorBt*$g~tn?s~Z(97F&^hb(=q)7^oIHoM(ueZmI@VTjysde{QX*+9zWJ z;$NS=i)r;w*B91BEia_ba=8dDjQ0I#{@&!L3p!Z?s8b^GtnGIns;jFv2LvAE8fs!j zwKCk!Jwd>(1cy(zGpctfB6@FhZnsoJ**9*V@3pqF#*B3w5*ba4Rxnz1+*w;{4)x3P z)XP=0ua|Qb%0J)1n_Ol*MYp$9%s#XYM=v%lUv%6;GgG*<(hdl<4Emd0O-H38wu-b? zK1(_re`!6e756;3sc6^99$N`LZxZ%0csZ^4#;762pvA--b#O?FbkgB<2oQB@-}SD5|&=Ar6YaI`g4L)@F$K!5W16eD7GIE}H)dAS#FR=tI4L1uFSumc-nc(N%f(&4D-$*IkvnqlQDOfNe6* z-E3#T5HSd3(bB4U|9K&pv}@_Mocs(Y^JG9c9R))0IV$&H0{=gIJ->=RBt5-K-t(qlR#HDCWhF-D58kDo%8I0k*!Dj5@0W& z1Oi+Yf*rPhswZQL+Ka0j*DQ6pBGCcPBkQ(qF{V?~eMKfIe`pZ%J06_ywiS+`^s@$c6k zBu>ar#JD2CnhXT#>SN50Kec7#u6#dU^b6c+4z3hf-<@v^%>ew{xyF}pwRE?^BiP-| zWNEOoU5C^0pKYR>ZqBY6Cy#^N%}4yu`{k;vC+V?R=-7yyKoUCC+w!-9$Hy~hbYWXV zy4xkRy5GKKfWv1@advg7P-``zL*x5M6;g$D;-wHibpVX#CjC39%NX*-&(}yt%o@`u z5hoGtce3W~9WstHqRkC9Ol2{Pjb)Cy;zz4e{s8wN>Mu{JZj&l76)j~H)REIiUI+4G zG{9dyI<}we)BV-I9Cr0l{Y#poA-*e+-$kavZUPh?Cdj1bMS#g7-)6YhYASwzfK{Cw z+FEXBV&&rfD0t(GkBG+O{r(F;`ZapRT+*F`62_+3?PT-*5b)Chw(?Na@#w zSOvN&JK~+qcy>kUy-yh~4|GX##57;X^+SERWIJR$gnwnfd2Mvb+k_HUF?uIazAwnL3aXmY8*8jeIZJjj z6;GXXNNKrQ)kBp3X7TMWBqiF-Zl=@9{o^faaM)Af+)6^bvLen(1UUbosMakLlJ6;^ zvZ}_~L3vXWCP0$x0zMt~Q_)ypW_)kTKqQ0PO{9$Mb#3h2-IKi+g=0;2RsB1jILg zT*lsS0d|rRHz5(hNov?LaYr%DVG?=5CLZ@6@ll5lV^-KtXJ~hSd^$llIg2`qKA9~I zK8HWg=mo1U0*~kYBr$z{l}~7_$~NCeM7HiR6>0p;5vi5+v9|_IY^>CO(rel;l|r`> zv1-{_$DdSJ%xMjx$@e8oD5SL)nX|%vY;RPiggF)3-uhfE-Z*I@l8`{(@;@{0J#BvT z#_2I(^^iH;5y+KgZqgrlQaq38Y#nP)tmhRe_YI+7`g^6Wrss|3f(`DRk-H^V0&5%9 z{`zocqR0@iI~=Orrc^+>{Tt({>a^z9JLN&0i{ycK)+-B-FodOSynV0LLV1yb>@@b< z>W|%BEvM%XSuVbQ=|Oe!cN~I(HR|;)l(1L}8LnGz^t%82T)2op?ek;sh z_n)}m*DbPj5d_Q}&^ie6YITI2J_g+Tu9PVcwUGHShZc!H|C-An)nU#jwR^sS30@oE zNDPphBJMfq`q_~ZOsyBND@(eA-I;fi zSP(MPcoHdzCRpA8^Ux%aJPVXCA9+x1Ug8{LHtxk@_P!k374EjGV{>Ue{C#C!C_e3Q z2EEdea0X(`cOy}|`mU>%sb?z$s^UJJWOGga1RWpc9W!}D4c6Qq+;Ha28}Gdw&^es8 z;hXF`zs%uz&+{gILHmwSKa2GFOm#~9-RsGOKQ$J^-{{ry?3bHIsj#D_6O@JN9a_&q zO7+{cm4wcEY|SGg2w0Oo)tEOGrWp8KC0>L9c>Je4kBjZ&*XUY0@$O1>!)5`qY(#5CbYi*`DYG*e|+~(v;;hre7-gO zPm*=OCeFDN>AS)>D026vLBz&-_ee(6anMw>NPO`<7ANJNJLIo|LQlg14lE$urG(IGsYeUlmI!&;0b+Bn^t zKW(*Aa*PL>DPj3(a-BPNc60smNki_#zD{wPCC8tXd}tU7rc-*cB=FJLe2n22cmMh< z-xZY)K3kpmmfv{C-ACY$<@=j=^K4~|ZBj-FHUEvcnm`E4(NP}BC!SppEU-3-K_lap z9`VQ@S|;F5#zC*x9?~uIxA{WDu4rz&Lrn#H&-#9FIn;=?lo!o@wz7H2!-&&>Ytcdg zbhe|>FxeTMPwZbAE>&lv+YtVs7nF!ygxSHJDa&=`1b>WSISyN$$tqe7 z`>ZB4^s~ZcI=#qyFC(w*Ucrn)f|zRI4Bqad8m|N~%15J(tbe8??L4)|_K2Ii=GyN(`>V8Q|@&ubCL_KYaO?zkP7Jr`5gybjfwb|x~Y|x95jwg5jcQz z;u21EGOoG^!iQDwRQ>$6hOKLjpUOzH#|mR)2IuP!cQll8omX=cw#1K+;@DV)pH|PT zOPK4wy(a!aMf<6I!fEH=#DBGK>!#vH#5Q|@^4LF_gIeBlF##dNpR9wkPlHR(j0=oB zS5Y>L8mb;a3r&q&O_B1==BrgZ5*pjn5-?Zvc;HNZn$WsGR?kuMGO&o=4Rkbw=hx0U z!1%oMsbo7^qR+l`Ogw#i(6xf8AdD=4E!TX4 zGzvupe|QsEkEw9*2M8;y&H-GsE8fn=uYbv+P~V(~NqyS78M#>AjrCb>w-$)@LZcLX zEqoJl|8X>KQ6LM_|9r9ZkFg(;uzLKK`b&{F%coc~qof~#Pc0eS3+pd4f-k;*>Mo3AE>v=H4XW5UVEuh=`ls$!F7IA)k9!g5fpbdK0HnqSz&PUNC&sH(<}#CCFVhQO3yW~fe<$AvKS|Yp$jM~bXTK=*ay7!U z!X$Daze?EgW#}i>)RtU8pt|>^XkMy(d{dP zsl*{J*f86x{TUaA_-mj(v6%)96Xo)`zZ`Ts2ADS#mrp=O*a=G`nbqJJepa;h36)|| z*6MhG5t?_OP)?c?IEF$_JV#>9u^#qzTVQA9veBmN5}G`4p(Y%wudLv-_(-YISL>hL zXgBa0>y8k-5tJyIe(l307SP7Edn>9t5qSfB_2q*YdduIrT=EWfR?o*-ml{dK;i7X_ z+E^7oPC_K?3gB2Kf^q2XD_TQq*(sUDVvp=`rb2x$GObGT-u1W5(A*}1e7;dus{+&Dd zgS`k#5s1tT3+k+~m!#GLzd3PfF^gX6L^S1ovN?ncA}QXyLMtCXO3%d^!!mtJgpHyzDD()iNF_wz$~Tqau$&$z`6$+r)Xs% zgl_HJEb9w$Ruz3!hX&2)yU+0{L zED06g&FvaJZli~8PnQSZFzXaS39jFfh->Q#G$5z)=kmqX>%10Q%N64KKy|f(8^Sew z6xlAPDC(G>Xc>HUX)1yvkOPg-%(;>k>gVmVf8G@&47f1rrl?vWA4??`t9!-Iv3scDOAWbeV7VUQ$xHK|dN7jJ?~keXyIz*l%~c?)QQ z0*WaAfx86AcygSe0^=+>bphT#nv4+f**zr0IJl)=Gr+LDPNrG-XIO^UpS+a^$Dvkh;Gh^9@R8!sf?_||5Cc`? zdsj~)9wSY$rTco2zcbT=;qsnMS#9`(-^_Lcm_gPKo1Cmcit4eJdN8@bAc zr#q(iV(U`zTIDAq^XecglZ)3#d`D|@@>@rdgZ|i}>?YPG3aZ@P1eX?;j9H37lxh-xLlh`d-vq^nLqaC- zkQ>OT(c7cPLH!-Ie0@HKSELy1A%&;*!s41NP zSNJ8t@;1e)GA@DbPgMy6>0k;dxu+0Umm=(ohnwXGgSyS1Z)G#fv5sJLGOxnQ3%DJz zu=Y?T&iOJd*}|GtsvxW;>a8^2o?^G>Ub`yB(imJb&oH?*Zdf(k6-telH8~*q(QVNt zOxMgDqNCBk-|@*&;%H8K+YM__UWY%wHb|=z4P^b6ENw5a_*71R(_Btv@~JZiwQ1rD zX^FW&eSr2xW#xpAUNQE@TPqTtqo2yQ#7-U8PkCPs#P61xfeQC*h z>PqI&<35SFA`%mf!u$xU>XXl1cxe3lt~Sb$#B$oQzes>(y1csOu(ag(Lij~9WFG|h zSAdZD@@M@VhxW`CsrF*19SwSq4AmgC6O84BGV^><|$gq-5C#|9kw zei4?j?rLZ#X$-8rm+wOAIcc-smv;O8G}+E&EziO<;r~DjmQvMVeyyas9vQoHQRO%E zuF=7R|EzLf>7m)Sx8gi0I&{S~ZN~eaFikRF(AVXiGG^0UWbYOFygWCmhPv7(F@0*~ z-1^jJq$@sg+VI?Epqpa$-8m#z;DS+zIZP|Hvwy_Rk53*a(WZx_SGm_KrEn?+_fVw| za&-{V{9U?Hne-4*5vDR+>n5_t6)Arl(Lm0}S^fBTGB4fdu_JA01-R3pa!9b2Rk?d4 z!{vkg-G7UG z+O&w4r}}a(&*?y^iQpmyI`4l=kE@KC`of}9{~JqmGIgd%t$)O%KZfk2^pr2urHB%z zX}6u-+Pq~TqnprWGLGAosdOtdW@&%$VE&+glyTwhRsb`*K8Ns8>5X;WsK~v+)X)e& zN;rh0U{Ps@ZL?D71A)}_oYV6!$;-6QQ#EDBSK9`#}*r?^c zXLP&@hqoR7E@xujZa1V|CL{@(PF^(Wl@4axeY`SkP5GG@tqQ11p6MSt5ir0DRdR_8 z{E8-3#=W|DfW6q%weST~l*p_+md9A%4P1l<;k?o8NA7dJ=^epYAko^* z@=K8v{l-Qg(Vp@&M4xxQg~Y0m5_o^{6p;}&sYa~Gtwj2rPwRN9XGeyCOo9uSx%}-q z&5u?U=~b7^wA)7;H$p9kM%D)8SLW#{$!ixO5?Tgb!$>laT~HToP5=$!1%!ftBm2U< zGWpVbRuOiV+T~8eV2RO)lr2;i7I*UYtauTx0Luj4r=p%9iWZytXAzhJ=VyL0hASKw z+lP0Kir4HR_khjFvQ|Ne0CaFRsX-fSN%Yb3%l)XEEGUfwlWQ{Vhh((z?!mx`#an2e z5{<*V6Yqtx0Wtlo_FTLAyh-4I5eT$;+&x%|1Gn!9>ITfUIn{jJnu|}+L9Vq@brvc&nGmMN(}Hu@3Z(0UUPY0g+h9F6L2Ld~9TRJ@1^kbk^gfXAow zu0&b*^<>no{59s;^JT&?RE+AElDqImC7=2rTEc+JBUPwRN_P(8nYP%q(WtBI-1D)p zD8<8>Sspe~M=@d{y`!kmG*H1!?8{IfHirq~Z zmNx8GXuZ-}BQY2h9aVvg*UmGUxeKX$IPBQYS=p$>OrWYx@gK2;{1BRc%ZJN?%vw5V zFEIVdggEP~Pjw1=SAE1>Z<2y}zFY66khx$)5){i!U~Lj?ihVYU>rR1p%DNOy7Sii| zxh6_pRkw32{0S5qIuVHq#wHPo$WHTDXUcSoVU{LBnaLR*rrql458m^LKBj09V623@ z-Mfh+1!RgHoT6_xJRR&FoQVBouA7R{@_i;OSUis&ZB6K}0qM1X78%p} z$4!jjAATRF07$3T=olJQY>|Nj>v7~RdkT*GSaj{VeSX^C1OaH0>qejMQg0wqi znz?K?bM7fv18tFqS#f%T2a#7(2$`VA>PB-XC$8StTwN%Z#C~xFG<6(4#N`dyVA86P zMBG_uuLtXI*3rq}zlhfOXoFPA7DmF1&&}V5Ei%Cr85LR;nU^Q4BKV;ICTR#j9Udr9 zlKVS}QMglB=Xg+6JwTD(ZH7Y!yj>M#WQu#vGJCSK#c_71J81HO-9kzKpv>jXfPA?x z?TsVm2(2GgCT%6EGeD$ns{f%ODIPAx_U;cR=nNQ%B?CU^6)bfb-uCnlP#suEk~+bg z06=LSqr)BuOOOh@06PZ8CLwT6D^Q7mu+UaUtjoU!v6eap_-i5*c-XFU;=yM4d4;!? zA|f!gR6ud4Kd8IwpG9ZND~mJ3ZjoOh7~|9p-<&7Rh7IQN)@|?wy;*!v5i$xFxC%96 z3lq#gc*PqBru@M{Zqds#T1zjq>bLVV`VJN6XgqR|>?Z3QrIFAx+XrXhX75}+me8B$ z3-3WYqt1$3E25AetAfBKJ>o@fDC5UHD7$5|d$7~X#YWb&xfE>t{F4HP`!3tJ?zWA> z$)o*|irspD&&R#-rQ-S^ZPL5x=}4L1&YjSE6Kf{LD08H;w~2i1oLC3OlSeF42$a}z z^K1JXdG7rbIz7;3jl?0vOP~uHv26f}ls-xw`ZjwpF0|sk9(u{AL7$lCiwU7WWEtQG zStTW5SyI}}Dt^jx8Kxu^gJw`%brEQ?C+NGZSHIeRNylHTupHr6NZS6%_im#D(;k4b zrDHq*`H#F$OP=@2X+pO_c)P-6`{+`51WEb7B zggrAeX&SuTscmNg8+tJ3A;VGWP6$`lT-K_D_U>n~DEV5_(E?Hsr@=y^+<0q_|4(7> zyc8J!EVZrJr-N*9K?&6pCp@ zvY^Pdr7!kUSV%LU9OjGNxOGTE_PNDDgR^=!K&rNNbsLZb=GdL~KhYLB6F+ZAM<&Jg zQnUKT2c}*rzKgzTNe~vNAkK&+06{m6eb*49PTJB?5RtQLVx*^Wy=uK|m>4ln(t@Oiq$ z^E>_H?#sDXGvZXYB?EZX6$#ym{D?x?>46>~h|OS9-7lJ{MMB1shw6pUAifJexes4w_*^4+vTrX&+s|4=JlB0QRJD36O7>6A!$DnyU^1V(KjgC3cZ_!rotUQfOho zv@Z0AUSvWnEZ#iwE4K$0AiMvZ>LIy0#pw#Ax$No%;d1O`-L*!y)egsGG`&Ycl*Kgo zFwDZFmJ={0s6DNI(#jUA%y%tuP<3<&-b{t$6p5?h<9Y9&eh`Q4o~B5l=Aau%PKSAurH(ci4p^36~Pw(e^(94gmi3|hho)26-p zh(sBODS?^g5Tx5eTfjBFpv|*%y?wPN z{PI4rs9l8A7h4jCV%QiD#9e?`f7*K5!mDQXn^IJZE)x>+(0$U%TZ|p1LolUE-B{3i z^C6O@)9_e!Ja0FJSf6s{MgdGQi}UdVWHVvQ^FtQ_p&dg=#fq;UgXdS3+> zvrL4xr1Bqb8^Bj{Tj=i-Of)G8fSd;7sgbFX<2G}v=qBwPKBCM2s8SPgB{u1^4+3`z zi>m#*KFeAh<}HOlV37%t$!oWRb!|cT;@MvDBKBEDSagm7d={f`3-~wUk={Cr4vlvh zC7kY1@}W2b^98|4LMaKf;ro+dNlfzh^YICm!qzs&74LlG1K=+d9e6~4q-DPM5||`3 z$LumV2|#6WY>#L!tthAjQ}{+rM@fv3KGt2F%Wi%eVBhXYfh%DAckvYoCtyhUd?KDQ z2nuwo_7s#iVA@zsVa;OJKYpE5DBG7WikV9z;cyUcEscgAK#$dq6l)n1*CCQ};5rE| z2K)25&4L}82KV}>#=VQR>oGc#HW_IATX0-r)0?0HxsNt8(; zwrih#WDNFK?fU#MhT8S z{VAV*Qb!9*`RZxMbyZ_n8+zXyYQ8aXfcQcFB3Sd#~7Am1J&R&JobZ1~&y!|AMe2Tz01lehOsann@f> zL&AlRdjA#I1&JPDm!pDImp?~`=MN4PfHqhPFq7x=?>_wn#kKkhhvW2w)uN<=i5n6b zwFGixAm7zhKZ4?RGcBcayBt*OC}(^fJ0+6*17x)H$IIPLR+b%_$GRb~T&XCzTW~dT zZ?QTQ`6l_9+j@E+Zq4;zgb^ATUlNt#RwHZhPZfh$EqAl#AN zWV+;%Rb1iw0`zVpk`3qcyZTdEC9&UKaplmy>G8dweM zj$_@!mN@@WJs%XbXG}5EG~!53-2hlrAD6E0UU7pGySC_P$qmV|ZjmKqrac|l{}uBW z4*djUJ-OzX9=ARp{%m*cQyLubumZzhfbW4+So;OT1o{ESk0E09pjqS%LzZPj&3@w}J7ppoK(&UF9{ zELS=v37AhU>Ky=8X95K>6ggOkCxF!6-CtiF)y1osaiy*RO?=04&M5RVY{q%>`<<7N zP*+)a{7_CC4FX1BS`g#XcLu0wZ!(0d)i5r$v}$BCV@V71Naf;JRV&Jz_oqSl4~FcY z-YCY4O+_P>TNlxTR3G+&a0&*~g#BZmWPlq4hcN$p9Q<094y?h8EOm-(p7`nkLp1l* zlhi$XymYGH!g2@qxg^Ja!0Z9vBfii5+$S56w%Xzw*i93S-AWu1Us|{$*-G>_jr5$K z^hsI&SD1D8fgdMGgS?m?3V%A{WsucG@>qKg;Zf6)lW*{d2C6mZqxPE>xtMpU!L1qk!ouB7WE}SVo zbyRZ$e;kRlt&Gp)9(lg7H1+LiS27Udsw9_ViesNFLja8b>rDjZU$i2=b)c=K3w*W~ zTYT;GYq}1y36%4&%fn((5U`JaLXau2^vFX~8nADXvWC(Rw|z(^pVGOk;xX01s;5gU8d8}IVSpbb)KW8)HGQX|#a07vm8YVoC z0rl-G%6}*FQ=!);XQJ|&uOiGo~-36J+Ii?esWlcjC%9kW<#2lXd>H$OD(G` zL4@tCZl(ll`h0gE}oM`9GV{ibce(S1so#D`zxhvJbzQ$e`3bDv_)9 z#pR}MY`3AA986(nN*}RodD_xW z;!Jr^pvT+&iS0J;{F1!FgmovM(@iz{uM)yc)GJvta%0d$SIviKM%)0ISOQ?}JehBr z(w`@si-21GSEqeyMJSoCj9}RN1@T%B@OBne#tr<0m<~$Zh0^Hr zN1Cin9#hP~xAm42-A3P#`Fs4ZrzXVF`Khj}H~oZwm71-7GCx;-2J)9pSp@*S1hb^` z6k+cTw#)ae6-uEG@hF?m8U+PHFEM8*PUFoum zufIijK`CM_VdfP4YRo z+mxDETTm!7>ts!WE)9XTe5E(7u{CPD_W}-y4u@J}wU~a3emR8S4+p!2^DYvH#$TR0 z&f~3rj|Jjnw`oT(jX5o!dV0}Z1m}+Y4IGZ{SFjAcd3>rRoKK6lgzCV`#f{$IoVmt`M3hDnkV`M!Abd@u8x_@-51)o!$AqWWWSsx8IiRqR`Hr{oO>JF(A0&9J*~rJ9r_=as3;iiWs?MKO z#&;^EcjF~W1^tLzyQeVk!WWi_vuvIYRU1rs@3p%XCW)qe3aR^8V9N09Jvuyy6t@8q zF&0{T`qeF-enK_2)5I%s9G_b=&M%8_E#ZeWok-S&9Zg6CBKTm$-nw(zMMrM{E0smG8C40+*#`qZ(=4vxsUJTeQ%*}? zd=KeY2qcs&KW#t_hD_S#n2lzX7#+;0Cv>vjcHM3MD6Rjy!)rV5X)Hx&G${$etW{Iu zmxM3^2uVcS+0s;b0ywKc(KCdg+a6%$6H?dL>T!|oLFcySI}=LAPg971gzid(LV*XM z^OReBYL%Vg#GFgCDvs~F`N_Ob{ihUqwt(8#4CqZtfM zMYcJqjeeBV5pjLz1-40AAur{Jyf9^8yfBE9;6$T1J$(DMj|Q&^RhGU>-hO{v-TJWR z`_$yA!l^D6I$6hzy2R9tNJUmEwnVwOmkjSZwH)T3lMCMS47|@20-jSUGWb2_1M{DV z8_iPRjDs$W49Q2K-ooNCBs)EbMC&(VK`JsR3EZ`5DarX1iw8EN&t#lcn)_;lJ|-04 zqB5_}%rGzLtK1ecj}d0Iix|+zOj(zgY3t*TvTo`T{I} zW&@+w!7xb9h@?)8=rM#(LA{?Ld)vM9WYI+OklZ${o^NYLYUn@L!VcuKf-RElq#~G{9h=n|OCn2V@xF z9+W@ybzEx(JSrs3H0@n_(#@s^x3KBOY88j8n0()w_uzZFwITdOVa(-T=6(f>k8BiJ zzcv%Y{MAda!hO3oJLO%gkAT6`rj9BvN0t|n&o5S@O(?d&y`3#{v{07;AL#M6+Vjeq ze2%xNH4aCK4{G0X#B;O!!pIZ=_U~&=a%b#bw;Yz@{uwS+#9ZFd+k5|=q$1*r1fwKd zzn^YkWbnd1Fx=UT%+O8fT`=EAP-;e7f!-Y4)^`YRM3S`nNhzgl;KyD$xo-qxNvJG- z$r}g83*4|oyHH=976(HP$Ua zQ^cB_zJm9-LpHd=&Pk1j7?8?fMJF@yHC7IROyIC*159DY7eY_BiQcuDSW9||IzlTJ zEzae(qKoT%v;7^|&TdHtxQz@xHP~mwZiEzO-EyLn3i0W2vd)JrwVYm?ij`he}G zY+<&|@Ns^?e$m)^yG0tdsJBW@vN>J>@Z_M^VvG^1W^@X*Pi@8`L0DN*e)=DI8cxWe zzss2ViMUdx&%}%a@MwYIRc88S^mkKLB#Atwy7%cxTTjaEc$$_I4C|*#z+}2hi7%o?qnv`s2hm+croSL)Rq#-HbKn?{r>WPKc`*QAgWE{;gh{Zvi8QiD^{@$ z*bDgriim%KK4>bBJoQBlv4wJFh%kW|C>8fY0OVSjanCS(UZ>} z1t}cTfEJT4S={$qeag{BzE7^C{{%?BB23qk=AYpMt)ZiHYYc*bIWD_ zKW>>4jzJLYQpB~pwT52~D?7UB6+L^p+-T-+4Bt>1=vS3BxS|@x7W_gl-Rz^x>@9p9 zCqVAMOiK72_5=BY#KBX7QY%}T6hs%phT}qoWu1X8Q3geRzG)QJl+~CX9 zU0OA3;^|>#nU$c7$2tLceJKCI7bZA&a`4o1uN!An`zhMPpn{y3im?EfsGSr)Rc2#7 zc=eSZ_~eyO6je~L%zy*kZ8!$udrE(_4lfz0Tip+&QRYSxspyO6ySXt#fWqcmN-ow` zrU!BO8xau-OEMR_=-_{A5jH=Lja1LMehn}qV9T55gp zWSwS~5AcncIWvfqUvSEF0BanReP^F6L2<`W$MI+*p75}rw4#<}kn-z~y)0Li-%@56m+WB41$2hOpXU~A4x(Pacw?Ag1wli}$zBy4r zbh%gJyR3O}5H`$-bWj=fohL`4j(tSTWqY>6$?B8Jg3!^?+_oR5*27( z#r`7Ub_BCxl>1|R9ee0UA-yZ@8@nA>bWByJtZi_c37^y+w9OTG;CJ3z?33FPjk>&F zNRLTuEORaS`Zf?VGv|YvV3y|>9_QV$Ewgd0Dj%JZrxX=uADWg##&2$>(JmVb`M|(= z4RE1z?_31UH;^AE5HPV;U~n-o3Mc5FE=oMiUiUO3|9-3rxc|)vHC-RIe!jdBc1NY- zEp~ga1lb05z`>FNUWtKm6wiNfJ2-w#FW0uGv3BY<%?et2lsev>RAAGJ!cjHUoju@p z7a^bQpHZfH<+PKx(BHOuH-Eq~fIWr^{|AC~{ilD9DV+H1qg-c99WD7w}pDlaW%I zy{ihBqvgEHUZ{C$6PCoPCYMusj8u%xjs{!TCI*`JE@x`7@cjeeWaA&%ux}~aON%dE z3UWDx%VVPA!g=Xh+8@2-+r+e^7tS#Zef`-5ljeOaiGs)k&*nD;p^B&AM@**I%(w`( zyb)(t>_7anS=*O2Hj=CZj)(1G%53C>ug&SrW|9YOES;NqOtEnA72v0$x_1u5Tf%5i zrxskxx5gL`mAN=X_Q{@P=WePi7u8jOh1#>7=8m7gSp@n1S9@m}7UlZ3d&Q9y7#ay3 zT0%gjdkAS*G$^5zG*SXm(le6cATX3tLr4fnODvESSd=uQ(jZ9J5c}r+ulLx?rC;`! zz4wRLFCHEo&hyO89oKc;=lQ#OpTI}%8}G`6o$4p(7*5uIaQHSv(>HlnnAA;1efjf? zB=_XX)4C(_#p|^D8msYN{AIcVjPC{A@!d^i4dI|AUY5y@hHIQY8F#~xMj)?SEkAN; zxw93ryAVkBgN!!yU8mq7-9^NcPHgS-gF_Ih>85SS)FWS3Fhr>c>Ug5{L zX>gQUxk`*4s$xrP3d#hTs>hPFm4E+nV3}ER`28l@HFIz)7L78OQdi40QSZ-)*`h{_fB$cM2V|CbB%O zbAI%9oAkeXMF0JlB6vEzQd`w8jhLIEH?n|(o-a#urq22A&(-Q*Ik|9wtL_|K@sCHz zQ3GF;j-u1#uU`9L*5|;~#`<)&c=EsB=pU`4|BoL2KM(v5lHmWvkT%<@LeGFJetGYU zn6K3go%@!KaE@O+r_#>ez3Sgg9hK|By$LX_AnzBZQyv<4;g}yzT(}?83?5Z73>I-rh7carPsMlOjswWC)GHou&Ui_x~#O% zNNcytXZpKPwV7QA#z~d6!g|+E^~v|YSz(7Kes}8N$^<*2b1 z$G3+Bnz{z)_Po7Pk@j!ooDzTOu`VY=(TRZdq4#q&GH?Yq!74}{hS54e5Xu#6% z9P=4Hn%~Nuf15{un^w(~Ej=>J>pd}D2-*$xe;XLZ;XS2Q_+7`4VYK+$M1sCpIXq{W z`-eX#N#im6>{U0o$WAz^JQ;a!cJ5$zI?=BGoZr6TxVAs}e87y&!sET^%#q6Yqy5`R zhW!%bdrt(6ZofMLJxR}PZBrJOn1EaVKvewA+ISs*J}_i{Up1F;J3`~ys>3g+4F#4!k z;?lz{dd{u|>L5<9atZIvXs$0!Xz(NoB(9b1?-v&V4 zKh~9A_Fwr*ltuR{c*%Uywc)Q!F&4`eXEjfjhDuqo?2QKANuh)%y{mO*pg}C(;!IsDe-4xa<|H`Dlr5M)acTFq;&abB8GAAelwu(`rqmV_E}Vf4k#md+!S7P_%P zkHmXtYRs8)EXUKSV#tZU0k@j>oc;X~3B%HcRHxc`T3s#SsA?~nmc$?+=$h}Av6^=! zRk~NAPb6NU>Re!GZ-gnpEk*s9e}{DKIroX(>)~>dHrkt>wTNM&0hj(3Y2p^=%5Noc zX`i#{T!pA3-C9d-lUj!uji8_xSDw1Os@EDFVh~G6UV0X2a@O=r+xIXUaoa7bYdm_& zI5MMrY5DDFr-<`}{wv_sA$#vMuKRRn>1)(D<~K)G_;WcR2)xPjerRv&FE7la9sv~w zfB)TS@eOvhv)N8MsH%Eqi zdayM<+LJq!-rpI=Yt-{btpwj9$aZj3AL2Ih!*M>RD57Rt@bZVnEZXvqw5piyPy3DW zCHpZ?xNqOZ1~h(uKiRZUZnVqHTUyw~yM{6ILOr-UZ^qFryk8t|ecCOa7Bx2)>a+HK zwA-JaOmeNvxr!$u&QA~Iv%PCU2NOf{W$486#D^W7o35%qxG*(VCnYW% z%EL300gDzqA!9*1S1>H>vfqkM>WFQN-3CwhFxDkUM&|zQ6oZ@BgH(?cvGG-fEDdVH zVasZD5I0bUxoy);&3(V>?Qn^i+^3gw{kN*TPj8u5xpb>85zINoG#5om<<* zbw3Hni`PP46MOE#E?N@hcUkQ;dH(8Kx;2=a4^!!--=*83REDZ*>R`+S=+bQa&D6Uy z=FfI-?kpA9cIa|qNN+cwiP3p;Czl2sIco5=nSfh~R zo4jE9#X7npXr2O8yx;{V$^vfb8xudV_&n%+h?`Hk&mk-cpN%1(GZyB4x|t-yhdnze zs+7pfoAPPdbTi1C^4`59KWh8wmEf<8&kHp3^4tIb?ZFzA*cVat`X5JW)%q2*$I#*1Di}TTmr+`d z$zo~zaP|(C#_)qH@<*^B9k}0^$u_JnrOEio0P1S|2j)vL<~$(>YvqY(E4Sq1%|!|O z|G0@O>_%Mf7bWHL25Pd#Hx--ka#=|0@0TWY12obd?~EjsuM~#zF6{{%RqEZL^b3(~ zPPE(OV&~OEjZ_g$z-HnCS`GxSCGIzo%?;}Fcdnkcb*#-DA{sQ#j-;J@%B#GP*RY*3 zCH6QAdXZXIS{ELcbJHwDMf*p9XSozGERHUJ?ihJhQp~{VPJ6mBR2uTi4N!5WHd7^- z1V96()`yFbrW?|zK4-n=dT0Y_Lp zo!{3ePd$3GdQ84u3-F!XvSx35qu*0`?%*0JZ-GHVgZuIC6JFFo&Q>;R+l5E z@6@j|@sV(Xdp)Z2^Nt2#mh9nfwQQm?D*8<@n*d9Qs{MPlc@(_}3SIay>SY>XmVmZI zEWD8W%4&q2;YeMosF<9VtAG5q!OnbZzvx21*UI?wfZC3XqVn0C|979Qhq_jo(|>+j zmkg`(J0prUuCQ7yu0pYq&v2=RjyDAbU6t{#p%ySVz2?6G7eDwCbiz|%U?=&zsVH|o z6)k)>{#4wc*VTP%&}+hZ(PC>b5**H~llkI~S4aL>;9Lfp3NnAHGt>QQ)VK`+OO00D z);`vx{2-F1?-Q+Vbgi>X2=aSr(b?m?0=M}jJ`+~SwaS;@8@Bsu4Qx7o7hQo9Aq+-q z>`0u?sxql#cGM+MG@2i0oycy{j8_+CVD-zs4MO_J^K|CF&DYa>{?r2EvxwgHc9Y28 zeK9+T2ycjr=fGi9qc$<*PKi-R7`GEV^wI8BykEAhGI5r83OIYqu|j{J=Sk3MBZ+C{ zU<^?-u#BiF!ix19QF&kOc-|N4PbH)6oPQGmKgd>sv4zwO?0w6OxOwLL-hY2qK=X(y%9#82!-&fjczyG_`?|&Zn|I?6OrD~H2IO;s_5st}tTFB)27kuDI*Cmco zRBPtx>P>FFylB&>%emY=1*$ zt!-?&>@n$_!j7Lc3Rgigmvqajoqf#;Y#P6K5qdoGbG;8O^fHP15-xke0!|ebSu(LN z7p~s6Mo{w_@{PJ&g@2-9<^7|R5TInJ2J5|DYd8KRnFp%g8*@d@2P@eSx6FXO7r=w8 zTQ%k06U_h!O#7YrI@y$qKw!H%T)>7~11ano;2(4W=!*?dCT{^i6UU0!dd+Out>(6$ z;*bZkcVQNg7bWR|N6YRY#aNh7XG7G=TVE!=8tnJugHeX>LJF$_G`K=J?%E(2@5&b^ zz|l1qeWwqK$VdWL6iq?x!TFz3MYUZT4{c2veBMcV0{gV&(2kw>q{vU(!M#f2l;WCX%|&Ppph?-ZM_*M6fvp2XPPyerVs3*%l+@YrcKONPBqn^r zz{qU=nB05HP5BTU+0){_K;Y0|H^6E?UHASNObgvobLnMlQ_#J&GWnnP$TLzrUrs~^ zlSgy|_$c`Y(I9=&x*N;H4Zfatw>dprgD8f1OpfsN{1Iqn{u~1TKJmUt>Ug6yHaR`$ z^w1p`J?Ecq0a+t2v*Z)cc?qu-c94g=W49Hc1EC;wz%Hbck6 zC_3-J+pZKpY>jXSex6#DNXY3k_Pjy;9Cc_)P9t!_l|TL}&ti<9Q}J|nh5rDbg7O33 z{VI(JGUk=zBEo!~Ts4}Y8_U}tLIeB6lOnFMJXw@y}=WIBv(wj(a-oMY5k0U`X;;;mBD zc?Nsmx*kFy=sot3z#iN9;ry&d=p)->a4mBg$kR8G*U(~dd)evo>TRqS1MQ|rN?yJ^ zN5bW4rY;ELfH2f7NPHjM_h}uJEA_`!tpVVX*JvK;ITfJ335d07n7oE{R56x3hv3&* z->hb2>K0z#0SKX4=gJ<6@1l^bBAO$eFh`gtzQ_E-QyaEz zDBqDg;OBJ#h1z#Ks8T<(;DcvuNT`Dfq>Yst@l8{7nq3nuMC8rYNTr0Dg&yZW8}e1t z{mU{u>LQ9{5}!gX9H+{a&VGi&o>2yy_++?z*!{een^0x4^yL)88($2dQCNzh8V{J^ zsHul$NPeWv)$=+L*DUqjSrK53VF1TL{2UM~si_}H#>_-?PJfvc z_1SoU8Gn#E<(M-j=bt|5QeWHvv=UmYH0E@@T4S&Dowpgtbn)UDV9c8rwQtm)o#G^m znHbnBP2!sdvXvzhE+KbvsNE5=v zBY5_c1EHRf82)`+H*cvYw$^p0-y#Tzrb=Y(XioyR=u*Vove=s9)jCP6<2|Y32`b$b z+vZJ{rfNO@93IGAMbeBeHX0TgYQ~CvBP;-v9>-OneMQ4scd(94uwzUnS@K$(rh}Uny=8@wgp7@ zze%NQ*mCQ*;Z{MRakquwI(zJ)0N8vZB?DIqFh^!c?erSXK7(EZ&v)yf6 zK*_L4QeBg4T4o}fGZlGq8s#uJ<%SjEP(1zPn_e$-g?{!zH#c1Ud;cW&I^DW4*}U2JlK3WQpHs?tM@tA9D7;E&+;~j8wwO$5 zwqIb)v0Q_`Z(ER3ESc9PppJwOx&>X!bm&HVMwdIF(fOG;`n76KL+=jzPzU;#eMY)z zxwh%+KKtv_6(~%7?$1wacG-a)S>{-;hJ(~eCan!z@)tBE(j*goI=L>c)uy9y)ho`8R<|_=SnsX!cl` z?nNqfS@@{+w24Nif~pc!Sl1HVUrt$9xMCZ>a;stuco(|`zwXRk7|aTEbZK&+df8p^ zWxGswtT&$VbXY@Zp1TmKj?$~>$Z=4sP13oo3k)$>?~Kv3qt}78CFD}q2OjY>oe<&}Pk@ewdp6T8 zLwdJEQz9nt0Q(0Z7gBZrLBL{$pUW%sZk_DN;G+UV@y*l)G<3)_hzp57M)y+@F8B-* zjK_wkJOv0!Zz?O!aEFLwm^+g?7#1I;Z%m{S@&m>uAD()FqNdYuX78G^Iwi4<9l=Wr zgf+;8r>c%}!atTb7;5)wyxhY~$ZhA!IF^(!x;ET0c;|#uxEX`qvXQ*DzTs>@_35#< zp$c>)O>j9Gy8$63wMu(!f0^!7=$W=Ig|_+dBA4;MuI|CF|Xak>Aml< zP|y@JtGz+-sn|K~hwy+Ym;YTcLogx&*7@Yz26@z#rPnYTxAF$lj$UkKJ%!Q0=TfAB zwAd`w_zO{2#gT5^2|9$8r~;(gKOA?cc{uRhD;~nn7qwm#xTg2o= zxQI3e9J9<=K(T!OYYiLeR)mdCZUe#rCdUobJajO~*`2U_#6c^|LBv-A#1i}DWlt76 zB&9f=O5`(%hjNS@vHZ!;6SQ^KklUjkT#Fn1xCNaseLP5(sxk`J142qJKpCWBEm|1X zri`1_s~ysk#Ms%Q#xWt%kL_>di=xXljL)ISgFlNN$cAeUJk3h91akCeR@ zY@QVo#C%ab9=ck_bkP=}*b}hP_>>8(iCHDpNRsH6dQjN@-8gWnX$H;KR{MQ98DuNh=Wdbrp$=-iGhYZ^X4# zQ?XoUZ_GYmqHIGpD;~6NP3|Tj$ysai7w!#HPf1S#M5fos7gq%K&A*PLyihq{QQ$l% zRB~@I1c9_VowM!Y)loU}NKG2g;c z_$Iz)$@u_dCRk?aBu~-{8?y5w9Q~o{N`Hmp9)F#t9DT?r^n&eFjoU!0>Lj!tMC*fV zp*KL=JK%3&AA$5=n9IN|{4mqSurVqQTdGVS^8iN9M?i9mC$Kp>@8B5zvBRPY=z5^lbHxxZL&ibWicp#_?-QHb>S>vkf!r7?Mnlqc(VjZvXTnRf!zZ7kJbG_L48bZHs7vWaRZOrpUg`)-Nn zUnr_u++WXdc#ODGPp#c1-0;-LkGmjk zK6vZa?nPhJm4l%K8R1^VtP`}ttz}~u+cY&+%Ek7}R9UxUVpqB!a56q8M`bZIKK7o# zdN?xD6hw&F;I<$bjnA4y z*4i>}bG2y4xhn=mMXS|QuJt&3WdF{vfA;`^dfV08a zXvYt@k+uSL!m2hqLhxE*vJ$R`sF*3{>|T6n%!jz!5M$o|FA0PQ8H6|nHYi=G6v(=A z$zO5E6sj~p#m`ylOED8XpNAABS9INx}crLkJ z*6g6?!QMV#Vz`wK;- zO}ds%jdL_7AVrP-h3E0X<~z*G=WIeLxzjRnFbWRjTLvy5W!H!&5z=|%)D5Aiu+WaJ zQ7(Xdqfv&P)E2P5)wR&#%$s|-)BhTOQPt~^X&>jwJ)GV6=A8}8fs`K#88wE+2DX{+ zWTu0e@I1>+R?WlB{kng5vfYQvaOgnY(Ne(|3Ss*N^xUap4?~OuqjTK9DppSP5aLAG z;N?n>gXzOAUnck!t`Uur6u}6^cnQLe{yDUiuiHyNk|a&&das+y3hR>S28AuGa|Hp7cxkeOm76BzZP&X)ny=6c!ramt+;zrL zM63ZsU8Q&TKM@ZFJmbzG`ID?AQ`pf^cb_E0*!Nq6{VjIt+h`*3ueIA@k$Z9~AAC8z z+)7V8TDEfsG+rJx--Yk6-OoMyu95`Pm0B02{(K(_d2s{_l(~83U^a6(T*QvrVE)sUy4yP zpB7g%%5`=4bMbUc^@D>;GAcG?m{ie)cj1fijZ_C}JuJbI zMwsD9+o2i1Jg<%rvbi9vWsR$Q`4YKQJoQ>7EGy_ppVu^OF-oOMN)*(EP$0eLm_QBD ztyeo_Ds{c)y(VqB!5B%= zN!2@C}eR7?1D-2pj}-6 literal 0 HcmV?d00001 diff --git a/doc/op-issue-opened.png b/doc/op-issue-opened.png new file mode 100644 index 0000000000000000000000000000000000000000..7388422ced750c392690c9464d203840dbfb0171 GIT binary patch literal 49835 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%0A;?ySYNr66TyP2*V8SzkXh)4##P5JcaK*Poy z&m1ZxvY~e_koC&}2tm}fy+6ZYGEP)Z1j>L*<*vqJQ=*#Q`-lc9C^GUnwWJk@_6u!e zdGh^d-TyugVelwbHwR!B%4!k&L?0nlj*(?@lUkccNwuKo8po^FnCZ||#AgruFJ@?x5~f|ovGCc=v}}cv z>GJiF)ySm<-@ATwDVE6#GvSIsgwICR~pKU|SAc za&3=leDj#xhp+LfMb!&cSsm` zFPwW^bHxvVN}NqR6X3tI#r8v7Z~y(61t!QGy9w4m(r$Qk>qE=OVzd;^x?ZnD7cwV>J=gAX+v>%q z_FT{G_Hr^GCcG^t&qP)nh-5(0*lO^2aMBY0@p6wD{3n&c#_<33&2wxu$bM1bQFIVh z0W!nee}931oS8jB0$(0vJz##&KfMO^w?6*U9)&*Nt>0QGrn9krVAfjYbW^Q z0+IWvqG6x-+IHH?+ze*=^qs~I3CWFCVguGTO+~?_8*cZ$GJAnOc(nN3*a)6?zbpA% zW8(5lT~b}_ZZ+zkyGF_^{!%M{nCspCk;&I;wDzUv zZfGJ|b&usM5b2n?N#8v@P;TKvu!!u&N{Q|E*UdSzp4x`St~92A#C9ibe{RP6E)nsC z%EgUkK;TzL{GZ7!idthLS50oe5gFBs3rPLFj2S&Y8#4xi)EWKMg6pUK9!^|{?k}{z zjGkEB;xT=eP{0?HP{3nj7)oQKL%2N4EYbTFzhfJatR1e*y`TX){Jjii4Lm(TxqLDV z9Q&D`&*r+xn7kbt}HA| z>QhZ>TDiyND*ALvb|wZnjq_LPhVcj4|L~z-YR#gKyIx;hCU_Ty5eW1WD@OTNRO{nr z=5}GRIER2mms=4&MK#K-uG;=do-( z+hDt3q^pgCogwPmC~`e5Rckc^>3@f_E2)T#xPs3pZ23G7v zT0eCzujf>UI*RpMS7FT!T39HrC*=6n4C_$3f`?!#TZZ+j7xnk}!|cz1F0E*SWpORx zgxd!>#JC3q40fM__Vsf43LzbNetcGhd&Bm>H>;>efRl@ zp*vi0drEIOw&gE70&5qN)Jp}DE&TXY{gYWxY6B2@pm?VGbuF}vW`o4xB=B`bxw31Y z##+6qb$f+ckTF#4&%#xx(+$gtNEe;^_mK~l+A4~d%Xh?-X9@YV_LU-1#D;#!_Ay6d zJA)2yA4ry4qDZy2yIJlsq^HX8>8Pai;PgWcH8ey8VUbe{OlJE7q>RJe=H#n-AK0ZWGefi^J5y zwRVqN?1H5qja!}%bzJJdoZ8l_znYqTPd0_`)a_I`4K!|^p?Sfoca{k5_=#9kocYG& z?SbxG7sVHqcqZ>i5uYoB+b_-jFSGQ~pT^Qti!g-y$z^cE=3QE>T~Gi1zGN4UB{eK< zZZ}yIEa&NSKlS>8U!~{x#Q#hx*|%EohuI>8CSS?tU_R9!E@bX9Q?m9r0${KIGC&!9 z6!7@zaQ!N};`M=lDR8}Gr&LijX)A$=&G)1w!q4_}bAVFQtlT=q_H5;asoW~wT)VoI zh{GtzKak&cUJ2H%^Ap@ol%g^0^josGiAh^;K2WuGQZ@Wib8G3FXPUsm^kED?{oU;Z z``9>l?)7h~B_k)`q!7$KrnMnne<2VSL$NU7XrnKjqCXvu(C4Rmu6ffJ9verH(V=CG zrLi(gN8=?8#1O0ywT`6Fk0ppZya=e3>$iDmR2g)DA6t{iVP8JrTqdT~s`yNaPQsTO zN5ZcF1BrKF7+E?Dso1^$C8l1CZE=6r@lsKL!lILZ%LN-l7vM zTDr1$)2>??X>xW~Dt8x*YmUiHDC_FKPl;srLC^Cx#c0xdN#B3_PMrIBatxpjBJVu# zV|)VCT%83LQl?^dT^kp~Nfq7C<1WZsn+6c!=QCWAZ~EilE9nA`xD`4Ls(Z;>%TF0+ zYhSE4O+22oxo6ZmVAQ z4oa8$Kiyg%+5LE%Kos}VJ!7~$*VMI{!(!29{B@9>nv+~o_59H%W zWCr!|qO#kabVD6(^z<|re(oZ_o8Q`sG5D-zQ=)Z`C>4fXYguJL!d$gV!>)k&z?qXd zb07FrhSDJ`#q@jF?(!deCDpO>Ves}Y-M~lw{AfH597SdCZsnYmWB8nrI>Dr_gfgWi zyy&=SIotBOcoNSrY0#`d+!)BZ0IJbX*u3m1cHEYeQa}{<@6+mYw-m=FZj3Tyt8Mb` zqovn{AQGw4-5(f3-doFZxSqPce!|f@9Hfl?C9A7_cG{_>JK|l|zDhR>@BWx$?y_b2 zuEKEZoGm+QG39x1I4Ger(Uwg>{j~W}DQldHnr*(b?11{Sj`zhLt;VeQpW~NW9j`(Xr>fYIO)UExC#PwSbA=vor_E4KA#?lSk$@fDmB|{Odb-T_w zpSEE^A8j<(sTXD5k9RT2A}q5jC+oJv+NVvFw9}F{dgVpk{d9E+QFKFr+-n1_Wjk5X z#a}(1VZKsFt2BFqXS{uHojJe#VxKpO_?;(^7Uof5-`4MWX3ss>)sdMOz~i2``mp-c zLKnRtK5J+({$!xS*(2+-mhz(CWuHG7?|iGCKbaz&=f7^ivRlahhd?~&^N;wmVK5P@ z{hx^JJo@x`5w^|JEgWek(Iv-vN?&ffXSf7I0_(}gtQj?Tzb)LOcZ(Nk6K8F<03WH& ztS(@ljeAXw=jaT|LigZ#)cSD8Ue{GxwetM%waHnT zkVFIx{)KtgahG@8 zaknTW9tLk!IxE_YyB2@1)WksDm;28|Mh*>-Q&BdLu_>h^81a#=D+O~^a1qK6ik zl!JN#9%TdQt%P#cex;&o(I1I@n}zHGK5w^KsLY-3A+w(wPNKH=6&OBiJL|+hN>LFS zd9?idBU4H2`C5}m%&*xGhRxTy>?l07tB72HNYt-jjHM#c%{7eEubsM2)l^CHzg?nV zVyrz*z#T`_G|9sr%MW;zj~BU=^Hl|ftvGg7a5sbC$@uItoRmShbkU^GC$J_uJ{^i5 zB?C?uJ$SE=J2F|@(Ek(1(?7|KJ*f2?m)^?##TM(WNYgtEfo_{h7~MIpXIpLEWWUmQ zTc4p~3d|+=8%b5STc*vz$|{UeYhhsJwB}Wugr1D!o+kjp#;vL`WJz42%lexm{X@Bp zXx{Q*`!d3uNR5-wdnSX3i_$fd$S;k12EPd2Fe5?fR|gl}Ecr5+A^7;OICyvCvW^KQ z;FB{#U@0Rv!3e1-d6Ujqyv-9g8`cBO*=xqe=CbiCX zCVg5>a0rA6g8jv8((_N)CwnYPXi&Iv25pKZTVqyiUu^#3MM;bjmc>9YmV37J=eP4$wWtFuelaFeEh6j& zr*uvJ@^C*8SK;vQ7q1Dk%tIzGBhU8=Z1Kf7LdO_dVaT_QW&blKyK?AjnV{5kAz?lmo_x+&T*XV~Wy=&|w={);Tb6$GKZQGO?>ZgU z((k>?Qo>LPmM;+2_XrGzX^h~hnLmC1sP>gpN+6nd{rf*KY3uKER*1+J1)Vbyk_^hu zy~k$qT(h&OuG#he%x!j)#RReL>t+NX{yXt;-_-rD*YQ9UrQanJB|SCAm1cX-Sq}k9 zuWjH8++*aHp6g%}`M0$NbF&G&Z^gr@8U+3<4lC36I9w#Diqo~t>x!7?L zuSSYx;)u28Bz%y;>F^k{Xpco;$uC7RaV&x%BIrb1AaeifnSy=TaA|Q23cbru z1mb~DLbxkIpmXiuD>@Tb+i zr^d+#Ed(`~GNg+kz}0!gL z!roe3M>~R-GfAdU*st>YCf_hyrkc+pok13G-h&jD;P`p?XvDQ-s)Xur)5pR0H*6(@;XfZQhEw9#5%-%?L^ zyrjFm=~}FQ3k4x&QK_KNbU`zCKbc zs^2WM_ulA_BQ&0^(z&8b`19-dD#;WM7l0Pz*Isq8P&i-_c?moQ_-G7V}P%c-aT*{ z*N^dz^M3sXsz&tWJ?}^nkT$jEoSDkWh|t(5QDD<_Y^<8C$qlSevZ7786Xr|mi!qPq zU!25HKYlnh^WMyr|Co~J$)Bhh&u&g95x~3rgWRKblsxhw;UfuvWVThia{{*z%loxS zLDM;k$~;@_CGAXOGp?>)ny7x)YKrn3b-sO?Q$W0Ilp_(2dyc;nVOzJal9Tw; zaUI`igKOKx(FLm!>0mRh-9ta9gE}%+HN)wq=zu$F^u^7^d4<4!gB+>};O(q9(8Gl+ zkDWgK4iqna)PMA^?8?0FUguMoh60&Tgsu;zw2`De!q?rMxLa%cs1oTxNdySoQYtXt zF00jQ3cpX7aGd zW~3qYhTVg(0WxxeU=-5K@~u3WNDU^YN~Ww2e^De-CI$7NfN@Sfushc$TQy=ej%4<` zI{n3!Zc3oNLz3)Wm?e;N6&TXUkShuX#mGSG^4&a2JNE-($uip?tfUKlACT<;agHZqXufD$W@2#ofV zX|cg>Y4%0N%j4Sfltg3=rnZs5%_IONb;=p;-ho~;D+@oCYoskIXYt5Wv}LGpbHaTY zuUvH*W1Xi8TE_wb>}k7<>&wSQidYfTbL27@fQwn4gbbRNtThu2ZIz9B-YQ>Nl*_21 z$BDVKRVm(&cjyquDS1nwdGMbVRPC~-Tdos3B7x}vz8Tk@chLnWvIZ}5Yn+0M6l{ra zAgGVPRGGI<0`eaAaexy?spcYQpUu<9O0UrpM?}w9BCB8J>OD3(e9tfGO5AiURvd(G z7>7!%-r7ZZojbK?1l5r*Hw2_t6uQcfdWCQ`;&o%%Vyqr4pbxpp2y z8`7X6qVSzua~AykDjTkn^Wi>8J)IA$o=kKt(`pQjL|k|Jc9XdhUnAiaOQ5p7tdjlENwaLcgJ@^YZ7I58 zm7QPJxhqOf1f~2`s`Qrs*55~gLD8VlFHhQ zk0@@QHe63zbeH47_?dn7sAD8;KjkPvEC}(P5<^{CkLPMELTR97!0Y*i;s-D-T(!vR z{V!;xqtB)N3RO9soM4jv1&YBQpF3C{K>t!8`gMeEhYZ`)zkmA%t#_qb`FV>kh8KV zl4@g$O(H#ADD;KM`Ot^}U!}SCT-k*tPTtXS6C^oE|CL!=<-W8g-;s5Tan!^()FV+| z0qjV#^~TT-GEBo~iZ{rs6v-7JIk%sn3vGQ7@@APirG~;}u{Lg}C@#q}^+Qo&IVpEP ze8o$jV9|iB3FIvMtp7Bnu;8f#e+gy)?oS_5Z0BB0AWZWZIYe61yz_&1*^<3OOW}$f zTdXnNnN?(O00#-d7CKRL8v%hC%1Mmlmx`)HW%S=iawbU6N zTwA+f0$O64e)?#;xfdx+QL`zOn!eREA~D14l|m^b!J~>g1_u)&33iOTd?Gjz&X&B}^04y^}tS3rJ8 zH)=hC-QGkocytc#L}c%rbN45^lxRtTG zrBunxjBwFt&(YN(8l{fDYa-kwtRtWXw_UwdG)T!)6rYcbW{tJ?%Hd2-)W`LkO7VX zlK2yE%)+*3Gq#1Cvp$S77&f;_;>U7G4zQpDgvYO^c?wZn{~`kIq-^pX{GBcOcage~fQxmsaya}H_rGDN2yndZ~g;-Ti#O@8}&WhK=M)0pIT9}Fww9ynGZ z%9p;sw&WDc9ZEttmytq}b0>6M&5nr837Ib^W13ZQ*gVW~T}UJfCAATc5l#274VqZG zrNPuM3h4*J!Iq#Ti^~m%B35Y~u-`mX(w7Oc4@sLvTsE&CH#SZV>m2FF?s4eGJGgo& zo1IYNEBsA$uH``^3`+8tIbWYM&G2&wIpk1x1JAH=1vhLwHOyas^(1|!{TeQ^bn_q< zJ;G8v{8e#MkA}%a*tAF`S7>QObrjs?E2eHXOTP2g6x&i=Dp#PFzRVs5I&V#J=^N{j zjR#DXYfUzzk^+c0(_IApyISu|Th~k0_h9qI^VfQVFL~>#GxjjAIuusmIYvtfeRC!4 zcdCIxa~>~ziX1I5oV--i1{8}jHS|zcEmO(O+USZbuOMk|bFBvb;4&RVYl{iyr4jgQ zBy{v72`upYoBX{}7@sdQ3`-IxkwYE4dI`KixDrSHz-|HG^`^cZ+56aUU4^G>(<&9~ zy$g+`5O$=0AuzL4R!p7cOKmYPCrUO05ZZZ9Geh9W@_nyZxofyEY}@TwIN6PJS4q=loBFlZvgIJ(d-?+~oRio~P;(MUj1}J& zjJ4d2t)GqX&R9FWq%X?-lBQL}#g5uJ(!^q9`}NPWlnw>E3PpOx4=NSY{rD1H2UbW% z`c$y5jlVZ~HnP~^iyvfP)#=`x1m;08ca2VCmo_4Xj5s5f)Qn5i=tTbP+NGQ2`|!fN z11jp6ti1Op=2|V>v8dg}T>8AiXMiCgXa*OB1sESpB?HO_fcL)@z<@Uq&6R6noX_ z$%YiG2m!k=U{*toS2uO@9zfVJcr1m<{zZ^wjI%z1#8ln@6$R~Go-JJe^&3lF<*JpT z*$%_ntALU`U-8JklH8{$@$h#KJL#4fa!Y5nH@%>X6jd=4CERB|Kj3Z(YrP;ACzznYaqwV4 z_6JfqkirsPThb3gIFG;lb{7DMA{X^(R(39JGJ4Br1U9-|X?ET!$kFYY>X9A+n<>Fe zDr}mO6aUuNjdR5l^NuECX-~0gVoPh&(Ze#p3z&Mnc8Unc119hphA~BW-u9>M0r~XE zm=*#`$i>O4IMR2=oUhmUs`ojQvS|ep@KMp!i4r_3$S);U<^ESRx1j*B%#5PTHj7>= z5qZfLv*cI?jWuafGqO1CyS76F3mJ#*C>aFhOx*N44A$;`0!L1|=$SL(kf7Ds1ARt6 zBRHZPzfzBEzGad?aYX7w`rZj)=Vb&Q7r>nrqjvT$RIA$a^p<|K`5b{0ja937G%eMZ znjgyh@d*Z>-)%3Wv-{i05V}N-XmGXkZtHC6$g{mH_htK=u1YX`!r;qCmQ0MA-&Zui zl1`^aKY7!?B0pxi##vg0deY&Xu$1&u8t>{-u;79GcZWAVdUKm&KEq>=_NsGQ<*RWt z)Ovj-WIcwtZe)AyhliAQo4ii6ZdGKSk<-o}9M?t3*UX=-{tn(DoTm0BewJHunB0uXV7BH2S=fX~q>rs))OIInfGQhkrHjPsE zQG68u(bfX=uBu<%`*+_n1tOsSeuZf`c3IrUNkd%=Rpnt_x{y2wZ6K$JYtmlcZ3Mq}oNmVmBm| zFx^EqrWuY>eKX^?xv-ZFuR#!pdwZ4`z7?-{ojJm)-p}(El0+8#W@|1@WxB!-rdO2Y zgmhU9`Motkxo`^EjkzY4(}N&?>7jpf(cohY5Y?ra5U(7-UVxS3x`g&L<>v%Sa6iPk zQ95gY!R!&Y=Z-~e_7s~-a$S>+6w&Yrl8U=>9CYKb@hUfyw48<{@GiT76X+J=qB#7; zY&2+_Kr@+O(-)!-!M_b6A0lLkUgg%em&grZG zX>C0{nn5Gp?DRf{aw4v3bAs+I1#HrqB!|-l^=K|C&1yl<)(rz+ZLeHv1b23HL-)VO zOVqyFn>HRo@`YTv5lu731%Zc1ojrEdyAx9@aOJ2HK;d%ux=~jhOBIKbtOksxi{UYV zP9S&Wxn)&-LxAumm&1y+#JMiDbu%tMmVz{1I&olE%!)-;`8w>emo3W~36IGnkYNgk z=c%scZgDFU6lAW065Y^Ub}l%UY0_;P-hp|lXx$^hHwvT>IR(U+5+V*&E?ipUo`pJ> zqIeJfsbouHS-qgrZ?zdO*h}2;_vM_mbL-EI{#U=N&wh$q-5oW%Lzgn(g%;pPZCvCV zD2U62PuX)Zw~8jl&881BQzSFe^4E?9pL~m|!Y7uWAJvawn{#jSgV!!1fH_^C0pIT3 z=H1d+`1RF_1sB2yo3pYkUX2KF=GMkne*_Q#KU_v9PUGY0~hbI*IX2M%cLV$N>qb;Cyr9M1q^ zX-Y-=h^MEdsiA%wdZ$Vne`-D|BBS*Kx)f>nK}k+aY(BQ%T_1-ic#Gfl;0@3R3H;*b zCZY{!sZKEY_|W{MVU1_~4%b{Ph)p@tmSBSU8S&U((pBN3T4Xiqw|?hwzKbCQ=KSfM za0bZ44s<-tlxy5jNHT?N;riD@KXm2mSi{wRu`gF@hExFz7rt}xtxv; ze1YAD9YO9%U;DnfuTai@VkVT)p~BVksw3JEzO* z9~(m+2+|om$T-#z#|sVa$@O(vI8(&ei%B*`Ge=bytG4E0!xpIbmo8lY&w28Q`ku`; zQp(CKXht7NfG421i}$BRKB`LU+#R!hzqh!?8t17Rr>v+w6!;s%OSTh zN0s77eJJylYTadAA27W zK8(=1>EsYebwCLH0;I#8&MUzg*Fc^P6^J7|%CHrhGj8->(t`c{cVZbz$p!LsHaWP% z!a9T1My1MKvMJ+8OW5AfCyp)mQo$C==p0;oF|+g_K3McLyytRhI9U#dZ{4pNA$D8# zgXH6=vsV^VN7r+&Z~tjq2IJE^p)g{dGg9g77H}FaEqSb8Uw7sK7BM{+X?Ra>)Z**k zf>zv0v$k7=)-$pTlc*iDNhojxz;fRv!IMe2+U%2p&vM^Av-i`AzeiF}_9yyWBA-Y@{6Z1Y88#Sy^RC`mX!DgShOXmZ#zO`l$W1g(i z=;{cr!MQ6@Fi}l#QF1R?-B8%5z_v7tvNK=h8`>MLjE=TOo`_#1`gi2b{O16HI;?=* z{HK#TK@PmmhQw(%oOA|0?j++VXzU=)1npgxQkIBVH^_!p{hMh!DwJ;)TUtC3z2`_T zNU#gN=~Pz#j((;IUwvCB=Qq;PWWLZpfhh970#PbMNaSdIq(mN$G}4=89J?8B#(TQV z40<~1_n^l9rZ}O=^`u)urJ_$X7tH|W53Ws4of3x+<(TI)F5Y0KqNmAOF4uCFO;#d+J3=;aQ`0NkWmZBNx@_ zmW0Y#dco%~u43n*iJq`-{SUlG>bUF-U+Q)~cY+iu=ivP~C)WP;?q^D;e(FK`nW(Xc z{kJw8b*gw%joQDJ4j z_OrO0PQ#YlX3#~b{JNZQ%H9X>=Oh`I9C>v-TIWj!x-0dMb8`$JcS8fJYH3(Rk*JSG zqv33k;t)R%U2JxFnGKYzSzoGuVyC;63YxaKf4fuxgF>pu=%crfLazm)zMsYKm>tMA z6pPWMKDkTk^x6YUy}gs^r%UjcbH6d3AgNPRsuq`krSD0`H{R^lUHi2m6k_@@|# zG+`~Vuu0N~ur*UzdCh1=C^JwV+#?9tyQLm5X4yVMxJ~1I&kmGSQyR8xGcq|SuRWQ1 zrjmSv^l-j@?N*6gx72+0NXC{H+fYg@Cj^`5Un={Znn9_!Ay3+G_Mr=mV|~<^#tH90EbU_ zT<`Lhim8VU)sZ0OTc#v6N`ZSUY#v0SwUYA0Y>vuivss#Xa_BgEFp=H&M{2sMng;~N z&_Kcf6ZH`J`Ie^Z`p-#@@$IfEf6>6OvqiBC)LpncW-Nj@xljdm zgj|K9y;@ZI@1v*0xQF?(Z_^z3>Ot-|VyJgIYPeY+vTZb6vBv2#o2|ypC$^Wm>8n(= zSjGYZN%5uOqo`EQxoHlwkNAhpo?)$K^RBLdg06O|=xrNxaobmDg{PN$?Dgw8s$#16 z$sAoq?C8kT*W17C07luh3A%)?f$zn&@6{nzl(T^=jBmYxcr(O6t!eUya@=9sh=&4h zsR1`ETp-x(Lk3}T#3TW$+js%o?4%=fYQA z2;r?J7;AIxAo~VLN5Ml=6hYd$nwBwZ0_#f^$iZxvicc_)sd-U~qdWI#pfe%goW-1| z4%l_9BXWPF*ugf&&B1Y2s9Z`47#1pIGs+$qP5CGvcxK^g|B_?P<~3Wn+>?mGSn*1) z>TOR@5Mh8Rl#WB+z1C#l{R(~xjyg($z91bF2ksc`aldO2jN#9I3FnQ@XiN%CS`8p2S=@Xq7L&gpFWiYv=F?wGf%Ti4 z1o6cwrlUEJE+td=-O3paDR?U&u3G+OkcHk@*T)r25kB|TYtlGA{YSMY#z3(fnJUt&$>oP}GR zI-WGG2N0GXyJ;Yw-RpN=?YLR;7FX2at0ffV_X(x?o&cT3jZ$@SrQ9CvNi}C z9XpFkd;{YwO*6}_Zh@me@gto_Ei)`JpT{ZQkjygBQP$xdj-|&E1Dr9eeUf`#{!r2p z6+o;Fy9X{Cw-01c{stAlK4T_0UWPDWkUp1)T#Q_a->QTEVv>YzG~}vF=}qa6h);9!l({vl}{G+5SzB z8qemRLbUv@U6pr304J+m^MRZ{V2+L(Ahi&)J?a7DDN}o{tlCVNr1$S6h`)K~rEH;0h zv-|RzKNf-fK=lnl9WyF`5Ff{JizKv#;GMkq8m4M=a{4B7~{Uq>y}yaZv6hDW7uO| zu3jQ;;pjZD9uS?$({gyx(9xQlVJU=T=hnncB2ug~?7aDenPxU1{l))A3$kx#G<|e+ zxBp5a*8T;AivU5M6TdR%m|%mb?Xmn>RkSRS6f1%$<;*qixx|Em)CBF}4by13)s3v^ zDXDM1LTc??Ax9?yl3cuNc2nopit}vD)P-fnMV>&2hf)rOPwrKaUl1yMzk4_k5#*8z zu~W(YB{H??5+qU{ww}Q7c5iixPIpZJx+y}+_mMKx7MqB5w~iv|lH%H&meA9nx8pOz z8+HN7p+J)-=pacL2m99MW+>iGE)om*LTQ6ZoHsEp*Dz0pZI%ynOOy#t8O9HWH$Xr* zZd>MEgW!T3%Aq$@ULA!~gmpk|EUaqn$1h;@&ryOj{8VIHILd{kdPS8gD@f0LqQ>V=~hT0`@?>v^Pls(c& zJHZ_n?%yVvZGwOpHaVp&cNNYl+86? zKqf}urQpaP@e4A{sFWr6n*I$r_2>W(#i}^_OYZI{64aonZ$`=VlO$UO)=ikaWGiO5 zGO6_=%uaN9@4Do%MDv1X%o8VsIdVi!4im@HtT_>1CPfB^;>!m;CZ4<-{p6rk@NJXa z7E3`-O*BgIOs}5E1D&Ylbe=+#(j1L3AorX$2j#I2?!vyiQUKDvX1jj&z-a*O zT&mNcNNy;!$zWgU85u0oeO-mOAcRU|PaIh3026RF+9d5ZdC;SLTeIy#sSh8-Ae z#3(zc^HYqye4)3M*B zEjlkO=j9nLcR7euOZL9V4s9Zdap~SOgQ}{;+yCh5o{G# zD(qLH5475DT^f17&Jc_hzLw-l7m;%A!&hy|p2=!?zm%soVSzxWr{wDvd9JVugk2Fq z(lD+Znh^v)Ho{joU7th!xL=^6BqkmBXIZw|Rv$}Zn1=V5afyci<#-11B)Oc33d+jN} zVAsK+ez{LXTR%#YYG~Fl?y{D>&7E3~Etgv-cHq7xsE$UHCe5vpk~f*m-X0he97bsp z4K+hx3gsTyyvxXfyLasZV`nqvo3Go3qaSP>k1CYHy98H58egY5MUqR_f9u6tjlk`o z;0EX28Fboq;LazJ4mf92*Nk@wZ2`2|z^R{r_z&`I+)4VifCKK3)j&HN1Ty`kH()4L zHmK7o6>?%=_D)*xyO}2bktC)o6$Vt@!Yi%`p2DW7LZe_5XJ^2%X{VrQ&dWIx1(kApTg55J z-ML-Zps9t=rjS|t`)Hg{6o*;4b(Y~2f6<&{(}|5Ne5CK%lR%;Z2~>kO@;l3d4qdxZ zZ3x))@YX>cbM7)@=^b=aFdl+Ot>7A${4T%oJRxSynj9WW;e&ny*nGjs&%Cl0@^Fc@ z*5mJR2Y5-C)@5|zDCndHKlO4{s`i!6o8uAbx6imy2X-%=N(Mq_5{l(Wqbzbx^WOHTdi zo9a@?NnSgbr)q1^$SIJmwLZe`dGb;Z(P-I0GBUwCV&1DASerFj8V=lCtR+i5F{#uh zj>EOk(ct^m@UzyQ?kk?}WRC($x*=rpLDQ>7F!Nx~=HZ(N6w5RfT>>ToRn9LIh z2G?QldzG?dzp|c_<4*g%f~t`-w=9_jq*fL`*HT=B?H+Rmx#_)6$$addLf>>oXyy-e zSB)#UH~jV6GI4@bWFwJi*t3zb8T@|iF_OJh!d12?5g56#S?hY7&;qhYjqX_@3k3ky zOMVdxGJs%!2+rIOXvzz*T8gE~1{J{0)vemL(?;@&9Jxv->Ck%Ut-RyH%*?lt_cobV z0ov?{?X4JT+@AQ=9<70A(!@`QR>Hxqr3FE1qzw?^_lAT)Y!@|?X80G2u52KMlSf=( zv3D1Ew5||h>oqtnPbWpMF4wXUXC>U*xscb<(x9p=OPt_9U_qE0J`^#}5HDhO_l^@U zhmDOd2?2N4>59Cb+Qf5;a65-E^hRfBcek^Kbjrx?Xe#fAkdh*#!hXTD3*&Qe_h(Y0 z0fP*FEh)UY88j$^%F+y9HCGmxNM(uW@ZF*8|FCt+NOd@X1mA{ijAG{T+U?4dw!W)7O%G$`mSfi}UNyY24o%kYe<8mDx#{a

QaujxWUjCs+8_UvCI%asC%M{NoZe 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 literal 0 HcmV?d00001 diff --git a/doc/op-mr-merged-event-1.png b/doc/op-mr-merged-event-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a4f82308f2ff3af4f217e378c96eb76cdc0f3496 GIT binary patch literal 56609 zcmeFZgZK&OP^i?>X;%zQ5qx&oi0XBWvxwX3eshoiJ5pd0ZR{95ggETo6zOf`;}Gd0+nd z80-Ew>O5s04GrhJm9(@fNLrd!)ydw%>YX_n8Zay&>5)d94r$NQd9I)?Ejr}Q0g&ar zv880*^=1Z|O37jjghqwGd@YR5OWGM|OYrWyU~gt-)CWvTwkyoU zSA5=X=euH?>o-o$OTOnV2i}|457C%DGLUYzS);9m<1q4k^*hN~BN4VEXnT(niiWTB zTE8&)*|TSO1S!Zn>w^O{AkR{j`r76B(p{0&uip?ZG*!0tEm2lMvK2ft(amrsOtefq zI={FeOcTs=+G18I9GTyOn3l>%@|gK5uaKgI#AK_{uU?>uB06Ht&?ReRuTS3CF~dWO ze}0wfBn~IwlKLLPzWj?oJgnT-&MrMLgrG*|M;DrE%=m+d6N&Y~YsFg^b3Z&5^}aY? z@N;p=m(MmGNN+Oak6SqfpS*DH5D za*f(7*^kMd{26$F5#EU&(uciLSCzD+tBq8#{8V>V?WjzuRb4RGz`m zU%W+Ade2n;P`M4);{o$yjL)=0odIUAXbPcdP5~Gk7))<5Up`8B`|vxu^;fR)M^tDb zU)8?hCC#2JEazJ3R3rZNXUzUjOyPg9KBRkp$QLW2H2Y>TLl~1BdM) zlN6^cG!vzETEMI!f_mag%p~6|pm=mk z*uH#pW~=eb=VB4K-NYT;jK;h?Pk7dotp~*i9S1Q7Z3k&L#7IBo8grKKe z9vg+1b<=cabT73@bVL(QJhs44dBYeWuPvjk@?8-O6SN2#U1+G~TPUZ> zr**8gTEGb{QWyC=l`nu&hPrD+Ym7jRiu_b>DlcQOlsohpR6pda zsa`*kFws2mWI_sDoEOBb$EWxOa$IgvyAhfgocKvIP%~CDZHkps2v?RbzrsqW>a;4m zN~CIp@5Hj);+JJHVid629f3c~SB;qU*~8hh*qfWpaQ!J9AUq>X>8k5Wy_Mx!;+pMB zw!J^moO$Jxx1m&qm`6P0iym$&d$gmo)Af^cYA)LyY_G8-DWPpt+zP%+y;XdOd@xQs z@b=4Bs}IJ~sL#=#zd3waA@A69;S~OqE?xFHBR~527J1O~z6du;SIS6oz2`lYNfd&V z61;TCS4zA}0x2%Yh-3<+TykQv0CG`~A(>|o!$NPUVNg6-FPQ~d#%s?{Q~9mttsaRU zKv%RXNBdHDLvKoNv9ev4r?|4jP5V^4W2$rteMfj_N1!}1CGwj^2)K;1d#XdzOwWGK zQohD?=GiR&?CIS2ZpWVDp5C6(5N}!nugz+4sm>CW{tG9nGhtDO35Tby9Io%&eOAr( zI$crD4eLsdcus~J^BdQ*UEM(kmE-HB?34WMt_e2zJ6eM7f?;}bbLEYN-pDJrbMati zhVY6+$G(TFE1MOAt2sR^DuN^EBiZTx9y6Zi-Y-rlkhVN265a>95A03tqt5N@>+u&@ zhBhD3)R)C__tY+)V~>CAGu<~8H`S@A)DF=uyQaEk^y|41|LuLreR%L#5)@Q$jp_4dN3U^(aka5Z zdr1e=${wME46HLRG{kvuBXpxmE)eLpme(tnk8zbIR?)u>TBq0U#lU}S9sIk59L)29^E49A!oN06ay2PJY6tCESrRiBRTTRid zTi4(nd5pBRX@UM?E7cIz(A4tN88Gl(_wG5cX`wMnswnktp1Vw=ET@#A+<$kn;E}1& zjH`7r+nS1TJf~Bq_d>5( zXQ%Z%??L(rTzOtOZXbq|7MQCI0E;rAi;447-t;FjmW^6hheo^pGyY>g%@$Zo)OS>f zRg%)ECDeT^s-))+kKS#}1Po)Q;SV#VHM)vl`kV)qKS|=kG7z2a+7@>1yBg~oNKaDH zH`cdm-gc{ANA6Uwwl>^)HO-YYt~Xqr)=)>MpDoTb=iC2YeC^G*ywRn%tEXi@QorEM zayz`xG9i9pu(jk&g~jwf8+AFPg^}n$hk)G*v`4L8>YS%AT4Z1?5IXc!MD&@k^y5AKur1Iqs?%RP9B_VCYobTqUOD>RJ1 z`Y7M$e_S#5>5no0${)gm(H`C35#1+`O!R;Eeu&I``0p~>&-*qsDRpTO=ss6Bbuu@% zbB5WwSV%DB-B(~c0Ck+v&`6p7NDn{|#$WgFr>r!zU9`bU!lw4NT*hYhCgxlowhn*5 zK@;^5zAxIEyBO1Y*uJxK7WNRM|Fehieff`SZhG23ySUhh(QAWMX{GI*%xU?#09>!= z#c^n9X+@pPEQBF4a(@lKzZ0W}xwtq8b91}9yK}koaoIara`OlY330#T<>uw(yzjy3 z>}ls>?7?a0%uQ2|R1z^y{n91--0d@I<-_yN){2cx7 z8Qud16y_fMdIV#=}_`ELM&CEpOf%0j5VF5&%e&+`Ymr~f~? ze@;}E&Zmrk_AC<+7!=hQ!neU0+iJ-X4;FmK5V>R7-fYjAbC#fl)e zirqt%`dkc{A!-- z2O(}5#>m0~ioYJIg7G>?Ba8gOYLs8n{?3iTl9-RLGgp7qwofEf@(!!kFAWOCeh*5x$8X>!Z3elrRI&@1&f z$ze!dC!P4PrGUiSsx3_QUmJ$7@QFdLf7=+v@Wg4_(Bm)pI@fRKh|%}x1bnVzxYP)5~yFjb)(?d|_2jMW90B2-E_Q5q(GtC4+ zdcaAyCrr{w)nF&rsUHDat?|YT{)r~fK}_4ivbt>W4MF@vrADj8x$qo$A zcxf%(UE*u)){=1*7Sz2E{F{jS9Zg&QwohSZZ#QJ&`AG`PNK!-+o<)jxdvg z0$nmwo0d^YzJT652lJt>*hs(TwbM z(@Nks;}tAZ{ndPJu}gBXan6dTqWrTFs#R~Vde>X;f6b>g+_>X<`az#gOt?B;o=fZ< zuI<99iVtm>zd(1|u{|@=m3Q3ir)!UFc^@|i{33iHsTJ(zLH4sI=~MJxQVcnzQHBxq ztDlYt0r#*uK-QL8Meg6E23m#UaJ_RKpBipMoWbc&@1rQ>c)DLNp3~H4-z@a5*JnsN zhV|yr)rG?b>y@AUanA^cbga1=?_^bgvARNb6D{?kJ~J!kfK1PeAg99{`vJ1n>%DEi zQM+*bj9uYV9-(du;HL4|5Y>DnNf#(WnG;NDc1nkaT~_Y>$C08 zylzKSwL42nfYGBUzaQ7jx$;v+m%qsBPiLYT=5A^hRVzeaSEhv~yh~gDz7l^$ABQdn zAPzuH8vlnzZEJlPAa8W1OHD4Wi>G-vxTCTl;-nU0cw6P1+jy5HZI;A2&FG=1wbqY$ z-b5#HC)APV{IfJg!6s7TXqnxpVJ|$C`8K1>@4D!Tfbh{RUzQUV@aHY+)z@P8xYCmK ziybX?d-@lV&P&mxZ>j&GBf-#xb!p=PW;Do8D)vc)XOGaM&L%$LMSlyhhU!gjhyda!c+1@QIgM+;`-$3-m&Ozn=uHARJe1#)*e|32*+ZIC(f*7f`q zB(*T_OIM*TmaRnvWPF=&h-mb!bdZkn2VICZy}G?9I8WubGLk)e&*S(BeAF~sR~le+ zy=+;(SFb{Up4$vhTdBo&3GkyAVXuEPQ9x&3S~K2qc;cHYz0-X(QY?FPn(S|O9bbP` ziu%#~t_)vkHMr>EN~-&B<*_`^jyfN}U8=Ck;)Qjp(%54tG5;H`0Q*P~Lg1Z#~$2d6VS8@B1zdOe^$|XlrcZ<{MmVQmfp3kkT`p{YC|zg{rRtnl zs$@|6L3t;!?i$(#p=<9;{%(w2vlF(>yZ-c+=x<-2*}r`JLz+Nw?55gov29PUQnV*> z5@hQ!feQVS+=T^((Ri=gcwVL*q#dkL&$&8pb}0gurg+XLqni(g3qwV@AWuL(K)U9h z-R4|28ERFmft=KWN%CR6pSuRk26e80QhucX)`@cIo~JRx@~Kdp zP-a0y%3csT=2)VJyvN%zYRKg;)e%L(jF)2Z1hUQ~QBzUeMux9w-*8D3TiUVVHGMa#^45H&+zP zJR)2iox0ds%uHUgxx6XiDsU7V`1#pUyvatB`*KT~%jzdW^68&NQ)} zpEQdJ$?}?<+k1%Q0o4{`h|EzXb_KOPldv^QMv&JiNge9Z z6TJRgs=)m!n;Gpq>v+^Wl)0X2PLe;{{wekIu*|L&+EKH)Cl(J}C-a?VQ@aQ=8SZwr=(AGvjq%s6_*gZQLdAR;W9g4dDFBN z;fuMXySA7luGOhemB z5v8!NQIp;}`yN73#R9HU1{+HnKVn!QO98uqx zpG)HoCdhhDb_~wPXyp4^J1ch1a48A9LJ3dB z$f#E zVOApR(2G}T;fh_TX|2v+`f<}*hr(4{^sMufdX0e#1j8HgjQktMK2CIFf5kA`5v;Ko z-Yfi|ldgI#@NkJk3}5 zJj+aGlav0G=>g_e;n>QNcg~8YZ@kZjRh2H-5n?+&L(ha@g;beEUAqj&!jWGFFW!?f zCAUx^c1xo(crkYot2l`jay6+J3V z&&4JJ6ZN%t&4FwRcKbv?w;*mynwIQTHwXJxoqb*)V1TpWQSHwuO{@Z%pUNrjQ3Sx@ zfa`OS)z84tpN8A57jH=JhG_1RxmcD?AUTLQyzk0fudO$GYY2xPlhl~}2YKJq5lAk} zRhN42#VxJHf2c`vj{eT-lyg9i-*0j{?z5FgtdDB%4ZzX-!GwHdc>PhAjw?}nb!T6>qR3dq%jzT zIxEull^-pI8_G_iAZzGEB?1g3n(}O~$?1dT;(FD9H^SscldGFuqBh@}?fY0LVwjPl z-g`Wi7jl$!(l(!9ayubQf~tMfDhll}wq7-HEVu6Q5*pnuH7Og9aW|eG1+`hsJvf&^ zKh5)JJibj_|CV6IWhDIpa@M=2{G32niqcTizJgpT#$?#rTa{ z#^X&;$*51xlhF~8DNh>tjrVgFRHnnDzIbI@#-i zmSv&!k{(4sbmNBS#m&J?Aaq5~U30fPNQJc}R~!BI*Dh_f_~ZjOCMIJUXIEfS;20GW z_VfwdEETH(gp^RPM)bks^fSK(#N)CTDrtfBqvhqc6&i@tBO!VhElKqnq0IFn`Aa@g z#lKhNb<*Js2nFl$NkMKw9pa1vY}?H%!=(6{z3PiMlYZ8Q;K?TD1jZt{>e-6W{*&CS zNQU^q>)f1f#VUgC+vgy4iOYTNRvnA_(lBP@3U_7 z0y>+AKf1R2wIc}F`q(dTUYc7DAVyRJ_?r^^^*9r@U9yD*;yG$TmU)>!;(>BCorAY# z3UiwZD2DohO)|&uM~jL0tvBp;>e@7GmA0Sccz>j;vybNjXFd{w=c&|)6yS3HoPDaZ zS})n}Qfbx8#HDb;0ZP$Cjq~yJmcz)LpdG7;bc;K04GO!O$oQ87N&(55(h3t#YhJq7 zD~TRT_Vta#wXccdis!WW_0zk8w+qfH`_wi;Q!5X111@3AtcRSVuv#XNLd2(u7`MHb zfQ4tlAGYJkOrH0waQolPVfNDz3{htLMQ2uVg)*-losRe=owraQK=R%yP=ZkkfM(qc zVeBbsz`O?|b=KBSfYKYbX3@OSrkbUkY(@OzZ$(BI3rA;Z`E?{bu`dFi9jNZP`{sX9 zL2TQ9dOmtm@O|t*!bAag0_baH^H)vs4746aa)2Pi$GRXYviM-uqCru?3EH&7h!3kw z$n&P(^`ggig_W2ktd1uA#P-d#3*CF>y$SM(T7Zp;d&;eQMioD^a>$5>16=(Fll+6l znb{Y8Ax`n1%upMzRIqb(?==Z=VM@*g-?_MgaE4Pwd``jEJJ+y0qV%ApR(3`EMjcYr z8Jt#H7Sx53ntU&zV5$y(lDi_80!o=npQ>U*-DrqegW$mr0-uwiq6^qwPbZ4r-Kuc; z2HSJL^V&KONxFCa+42`I70k|OnUOO6Vn)P+MA|)1q?~>cuP;S-Dq@z& zvTo@`51r8mael=16ATF^6dPUDS&wUBO9e#DS+Zjv(0JWAA}On?xiu|3zlz!STS_Rq zTv5RMhPXpgDTVB0_|Sv?X5#+Jkb7GnWfQin@N2N?8ojntCh;(v%*p!4n_!w8Eq%bG z&-tN@cVrT*CEfrGmz~p2vmWOp+Qkd>bzLZL{NB} zB)0A}{#(Fai+J^|vRtb~oqm!`+&uh|{z+ZA%wrRmGh-1HbGbC5SZK6z*CC1k><|F+ zA}_zIaatK|{5|B9lt70x&hlTiy6O#RJc0AXSqvObi_=(HmS5TvIj|zBa#^i!wG*6#{lk&-o&j6IsZ1>T=Yd7X zRkzIxuf;VVc81U?KXnS)h-7&p4P`WzQ7=q|%RjY_IR&4-3iwIU-+xV2XGL+IDC{>_NnQyL+gUpBGF zh)G(eUc=@_`LiG`A`k$3Lb@%zyt6->JQWk9q8O_}BR+@8i<53-kaaN@BC(@1z!J-5 zFXSvSqoQ8W$#6&n9wdsY*9HBE_l&g;yL{rvtW9w0;r!On#qf7|%tr2Au8Q1|AN6M| z{oDNW;ZKC%=#QL#58jEQ`xX0KU^n)SdfRTfttL+CRfj~VNqGGl1j=FiE6WaegfX9{ z7Fgl<-v{)1(GI`fSjP9Y5WIWzM8QLT^-^azsfwO&Zn^&J>HU&sl^ajsV4p0li<0wB zySlcm^!PnjDax-`IpN8t6&W=SAW%iF4e(Z{fjN8O9JqKBzm{)u_bR4K1q`5bWgX)R*|1TwZ#mj0#kNk^&FH;L2X(B z69N09Y7~o?S!Zg;+Ig8eL+b51mbPpFhNO=d%o|9l8P^ot_1*=6q|!ZPBP}1*>VxZh zPn0}QW_O3@P9|Kd+a>W>rQXw30neT*Jk29)HYmGY-h>b=@lYBOQKfP5>noprmQD>} zRRG!Iu^eNJiYEn_dP)QkvN5dZ!YZb7r<`7|s@%Gl5SmD~`LmWkI`2R$=WRs7t5K@c zpF-veT>3~(f16DD(hp>VPcV)r=dfhxtrqy-#do}4{oc3aU|m=U=z_>k-UPsTmKlsofL>KuGNm@p0OrX?9{0$3Jtzi zbH2BMv!wiSczJVrr0uOI=JFuL55gc-xGa;OWHofNpMPia0?8?f+!gBLsk zU986$AsDQ{!** zZ5sY;PKb%#OWdkBCItt|46nSZeL+%H$_WRq+Eu58g+d@JqU z5c!RJOtafh>d{W944`l{gg;92sw0Vt*;n9zL_Hhye8aSie0#SiJIzStIDono`8|K> zV)zADG|XW&9wr72t#P)>#^Z)ePz){t-$NB=~)l)aNvu)-h>afq2@w zm(we2bkX~m{BY7k=qBdHTCwz%%vq1_jQ_V)UI~I^;vE^9J^=xd7J|FanpZCwLLVd{!Nk7g`6itIwUx1>FGvycWi|`F zZvdDO(GDTf$+RfxLk8vmlLkmp>CT2$IclMFNN`n5E=1w^Cw?((zr@bdfQT$YcFlI( zVv8!*ZDz%eL|tWUrac=?Wlk+r;lvXc-LV{^NxVdo*E@5?3pMdxH~U)fA~mZ?`7iPF zrDN|(tzB)oX`SvP?{eKYeDciTi~}W*^Qn|1Ek9?=Zfer+V*PDJi#PKgduDUqCv#x}thaVdFFvnS`Yzak=( z;^efy{Eq4qjHn;cCQk{le_1PfT!EY)&Z4lSpOdZ*?y)lJKUQ}Q%DyfPg_)S*`CadP zv0}k(n&=RdRzo>8dKhO+5qzVu!5i=ULfYUXPX!yxj2gE~_xy=R5nBT;9zUh#im?f4 z;x^@{D@!`FHuUbM^PRt0z2N^x?8=_@eICKys%*~*JF$MqZ^lm6`%MY|tw!iH&eyg# zJTL>TMQnRK@%o5$!}8rvG}jyqtvBkciG!1{I%GJc=O-mA_M-6@Wv9F-t&Z^c<@&?Y z`;dwPsD((F39*fhxLOH=0KAdKsX0n~Wl@*r79xD#(V-G=HZzf|D8!%^CqsCT@F#V% z?%jEaZSJ_X^#ig@`6L3VTcNB%#oSno7i^=kUSa#(V=rqsbPXl#Dj<%+aMJ+sxiO2{E}w26yRu?$9)KS@ZAdzpYH z8b&lIoj;_*0j8L89d1h~5!3_w<&PNrpq6~h@P!5XZ16ED=RhJ}36k-IqG&b)wn<0@ zLmJwDXdoZc;3b2`TI+N^%M%(g-I_oUYT%BxB_JuVS6w*E-6VKgmRuhlj>4HkKF5@{DGXdX3r$q6T(Y>AcAxg z3u;ej>&+8$YNo5$te7BW)9dgVV|u6>+G@d)jy6C{ILO906@RDdy`Mu z^5dE$(i2i8LKIr83HV0ohX)d_XD}fOgwWN(nlkdBe)Q^MQ}_NXEellN&ODJ$91XnB5*0qaa>NkLDa2K*;_QQCZp#$1| z>av+fMyyte^saML38GAStQ0aYOZEgHt+jHE&8;7OE4*h^|C3f{k0k%kup*#j;KP({wCBQ37!LJh6fGf1EP}kM(!t_N} z7LLN;lP?k8$*}-h<1=L!dTu8raj*)JL%zMzLGIm!GUisf&#JsE4*T1N+g}GMBu@!} z&qp~}$9|R$SoXE6J!JItY{}#6Q+1w3pAXtq2dd^bRoP*WfDva6$rkVe;9U6a9|^xwlwu(**XVtMQ{y!dg zFoBAd`YwI#?*1sXUqx{*Fjk2vqOiUX5(A~T8i1J3NGLLXL1UOT!<|ykQ7j8`>8`AF zk&5Lo(9dZ}m5J&SB|XZ|)n8O%ie4N2=(0`t7^bTkTVLIA+8-=f~bRE2Uo4W~z1J(h$BI{a@*t@CRMve~9Xwb&*#( zeN_T!IU(*txDv!p1v$#qpO_Rl6iJhSXA@MXqZ;5`td%~k;G|;RUY7+fP5J)zR2PJp zDZ*M9O1X6;(RLdz#8$82TgV0A(C-y1x_exNW(<0 zcntGk5hu2S1`;Yu_nl3zQRk$5iZdp)YEFd`MX;9 z>(o&J)*pgF=DaWCFsS(V#RCvmGCO9FD?hx5=ncOj80rsn3B|Yru>)i0`%u;T%96B` zE+|r>j6tFyZn#D&^8hJH?@OmVYRq;;Owc`13WOk|l%(5$&C1Yp~(hS7ZyglhrqK;;PZ z1wL@J`ERGW3IzTjufjCrf^00*r0}ENg%Lh*rIQeznjdR4sU*-W)5j<@CET{mhJz}47$kvRn;`HBIRU20n7ShHJV`# z{vvRHX3qZ=XV*!D+EuXF2r3~6vr;mx^X72E#W6n?m=33mKL{-^l2R}+kyB5h(QVJ{ ze8D{_QeYM~6j<#s^1Z@z{dC$WiOZm&mo9{gUPng#P@JzkC+ zdcpFgM~w9}{}v@fC;>d}nV`Inb0F@69)hm9jGS#$0$Q#K{gRUafks;S4JA+_N42jk zJEEv1tqa+{r8)Wc!TtI_>{OK{%d!$e{BRQ6>2y?Qm#DQqe& zC6e8X#hO>k)|_NybL3EU(yr7aygSM(FMh3$>#!xO!Zu0w0%g1I3*S?hP>JCyc4CkwWpI_ z+pHJ5g5rp+ex~u+ea0<41BOD@3k67+Uy&J^o-VF(O>mnL>(y?b>(K zXaihXPQ$CC0q*rV`__cL+9gA`S*v{A`SD7F6h6x+r3~?fp-_IIO&LUk69T8S4& zUteFW<7#(bd(e~I%uGq-PeyE-5{n^Zm#}%{jsMMQQq$e-mD99x|7HkqF#t}vu&Imb2!`#qG`EpoS!fGlkH23?#0hxi@^T9sZ{*t z{ZisR%@%H27}4X`*35+N-*LUX15aI6E5@BM>#TEv$9m#r{ndUWzX!WMteP?W9diVX zXn`Nh{|RGRO+5dpY8XAc^%-hj4Xg#dsykunyS;S-=~gNoCA`avJ^NTtK0^VwJc9IT zC5DD^c=9KWaKbSa!xU2Tkt)zg2xly8va?#J=klIY*b%684v551ga86E&!Ij7R)ZHZ z*I7eS;th?q?L}3`8mM;BajlZ+N+hqObMy=no0smu_W?Xb?)rcWY?jpJUnTuC)Pos* zmmp!cUF}>cC-d3s>*JB201lZi_Dd}ZLau2Er@naZXhiR=-Yhf-3lYP4OuKtoGJJkK z2L2+TDcq}>m{U5Nwc)Nc%=`S3 zvf}cj17D(-%*ba2==uxL`#KJ1{caU)(c!vlg$e zj7@E5dXwh9=orU(5?!g3*U9kJZ7SbhN_5)7{4WtPg^PaAgU$0U<6s+t(#z8w#Spv~ z^D_P%m=B0sl>B&C1{m$M&t@!XgsyhKbTm$1ob@NLFB}{kty7dShcgEL!3gc#X?}7- zl!Rsm?=#jCP_k}sQ(Tpi9{jRnnw`;Q+0F- z&f(^U*Q>i4SyvEgPikzx*JLf6fPR_58d9Is*SwLeo`P#48NT=;%{JKjCQ{R^P|p%B z#d2=llEMK$3n<2@UKng6Kz@>CJzbf%cB#iR=<@Wf73LxRel488D0 z$4_tMtoh!ahVK{SQq|VD-%#|faj)iop8;H3Ew?OQ=>%D?cKXE?+q; zOG+J6kzQWxHy+6O08noLbf9@MfB#0;oeiMKcc1(Ds&v_coEN`qKAl?GnEoF8>S%vO zFc6FQ4W7XjeN<`teAY=?=k#6|kZ4 zgM}fvb3GIk!z4*48OtAc)T}e62T6fh`W&|WuT_yG!(1^k!~hNnV=KZS zx-Ia_0PN&G?Qs!_gn7MVYg~KadoOt-V8QQAV*!jlYXbo=S$eIWp+A z#O2TIs7wQgubAdhL4sMQVZ70zN1+Vb`ozQD*iGYt}nqDRr5>e{c?yeKJD-1l{ddG$Vis(AJfwqJcdF;nJElHuf9H_ z+1ppfwH<E-;Lt ztCCFS5;RCah7$ehvuXOy`@ZrIUVz`_NSjqMz8njac}z^Tef= zJ8CI!{AE_W%ur$DC-rhnu1K@^_$1DH(~w>`YewUZD;bO8*N;?_CKi4DyFNOiA5K`d z)eFD+P{49#g29!HD#!4N9f;NuWnr|1*Neh3=1SL^dd-CNQ^2~#i=oejLSp>XCEFk^ z^Tk1^ZliU%G)+^nN!qS%3P$`-<)@j4J=O~i)d!J!CODx&^k1$YQG2{yd;a0=#Ju4i zUKm2a7D;)+D^fYBsn?at^mP;8BrNR3N)kEHbhz&=l`ZhQd|V4*Xzkl?6GG5Uh#;jjKn z*pH5lIl1f*3&N&X)lWn0HiX;AiO`i{1fGYqAdI}PGQ>`V(ULD|Nqtm*`U3(a1Ni;? zS_}bdu)HkHTq)vi$&}0fE7cXtUt9SZ{qG+@hKhfjee~hz{_EdY zbs;yegkmkXZm$m9r@ii(UBr3wp2xO;5zp>8q+y=5ho)%&D@eUXTRO}M-&5RU=`c3( z)L@x>O=s;2^Nhy#B-RRGI*&hR`xkrBpw}+ArM%)gce?-Yhy?P3%NwZ98-aVJ>>`-H z;nPhsB7XIbJgE>hp*QKA6|2w&yS@~nShsk^$omC&ff=Qh47RBs>y0o1d zP>J2mVYFH(O<$Ylj42i%hmXF=p0Y3prrCukSWE{{*w(}=F&7NkRcxfm_KNQ5`XwjJG%D#p|#=bszqQ_(;GdFGru& zM;eo+*|#ch+!kEHOC4{U7tP4BwsM7ik7n)n**8kQUUhv$dw3f@$e4ZFcV5!OM9c;= z`LNNRJa%-Qa8Bg`W{smh6khiJ(IF%KUFP-o9uyHcodGcPd+o;)qDZQvhXC)hUEaKE zC6bFi78<~#j0G)2&n~dWXUqtudpoJ@Q}+P=ivtx|B5tg&UOo=g<#Hk;wOC(l7frK% z%8d^@x7a_)>bnXJ&Jb1>*{kAbevw@K;}P8c#p$6E#pu&TOufIp`Y6Cb525j^qx5#> zn&Nezca(>s!aVVa%SJFBQ!v+8Fl(4K9k&Bu?K-eS;6GXsULtz4u!G`iqC5SW(`#`KmX0%Enw#R_i0kP*IevvW0t^CUY%F2n26h zCwj|oiW$lOg#-mLB}Gauf$}J%RC0#%Zc`9$z6*@iMM9sM$c)&W*ctG2FT}0%R8VK~ z9$8Q1Vwi`n-4Wq2sQ>&u?(#&3U>#PI*{8@TI%UdnY7|gE+7nt9*Jp?ENa=kWJgFaH z98B;d=g41J$^)6?7ZWW)+VjM&8-obazss7xKk&a2g%!ldD@k(Uz2dn~01;d$)+w1_ z$B4SJTM(NOOm$UDu!=LGi96of$>Bu`Uwt=~qpROe)fZ+{=S}XqOQGU-m?M=6DqCbk zX85SlFH9=Yk843b5&k90mtY9Yt(^-sbpAv&wgOQT-^-a?D?nG*_5q!Z#1il_X7Y3B zdU;?F0I<^G(LX=M#t!`jGDeE1c4YinsNG2WHXkB_T-0vnzaO#PdRmK2Ro>? zx~@6pCi73;%(o|40XH$i*o{J+uYu!CNL66)E!EBph@q zw`NMp*qxm7Js2nwGrHzu=go%pkybgA-0W$wQ1pQ(`}D1+yAb?GEov5P!v1d=Lj|Ur zA8cpJf(x!R*lfoFCQ4cmDHTU1?3Jhc>nuh;6pvnkMKe$_bNpvnCT#A5Q#rYi zZB+cSZI0&pa7PHA)ue(Od%3eQQZEkByJ%RjwJD`JZp*k%tu@pHNO-DY*_^Bu~A3hrN|WZ!WqBq7L=?(Sx!T1RLB+ef`)W}J<2 zdhG(XoyUD_)t6XJtP>nO(#xbLo7YKeTUd*i33~DY2(60Ef$dzc8F4?co~>s7WOi!w zv*bG61)Xbtr}9j z1PgzXWj+!3_4a73zE^Mnt%v`k3TmJzY{**y2*5R0e(dy{8sE53%6~=Q{yX z+<&8h^Wj*8T|n>NA|BiX2aKc3q?<=Ej!M;#vB%i5!8_Jvl;JveS9LOC+$yz3HH+{Fl^ zF0SrBP{W{0_uN{YzjF^xG{@`asV)KvRETBKQ1L;T-*yL>pG%jFd(zPCgyU?4ZT`F9 zBFTrnb|d)LDD(%kVlUdNuam85axHE`%0UyA;BqbNFCfdIxR0v3r%{5|3%F=YfkWQ8b z!TnN&R@-cWY_$6j#IXu=*k!w|A1~((ddy8)H~K3HrBnhORkcrVCkjm271k*BTQ4V+ zyH(}u+XhUDCl!LnQ47c~Op#eLv-7IxWm!-gHG$3jp7apHJjU);fhzBW3X>#Lejy`s zmD)Ed)m9W{t07S`quTAE&&FC3r5iU$Y3ae|!Qt5XTnQl2{9AF?RT#VU+9nrTDLd@a z|25BvFS-l3+Yra)a`#;Pyd6yE-HxtIg(H|s*j`3_%in6tKK66A{@$GROw|+`5=G~i z8@ho)-}q3{q-*kNbv$Ll`aZ%LZS2)pt3aUulmt96^IxP{S;#9$i}+iST|A`l)l4wz z=Gl*`c43We%QQnmzH|Qq$q?-_Y3``szRMo7O{*XO1Z?Y#_b!eAkB%YlN1{4Ly--8~ zn>yWdCqhNJoSmU>cNqm%4$Gx(Gz`aq#gK z*AkYMB6SF>SW=ADmmwbjQw&AI@&rZL;L}){u79S^N3OrPp_kMZdg=n zG;8-p?$Pmo~qIB`d9T{z2X-6=K}blSc*Ad;WymK zoVlo{b#DMnLs6u|c9po(7R~5PW%=$?nV=W*K_lXh}b4FLMvf>J`|k z@mfaRdlK57>WVqG;iz$j@Z7au*a!{A2$S&}UhVo!>ZuTu_{9O~XIX=|!m~&@UM>&1 zMD*uK@Jdg^1lA=ALa1jMHyBS9;iLL6!iM7egDM*Bsj)$9)sX%?#i*|diRLM$pH4K=TGn5_^1draPO%;-gR2Orz1}oD3?e(L6FckKqJ1rnWD1ZTXo4P{IoK?ZzlO< zL^mezC9pay>UBHwx)&EAEX)AP+*|c#eK{xyN!9C-#~RE-xL?2epA;W8B?2)SSJ7!m zNLP!~t-qEbH_Z^3!(2Sf1>U?PGo>t1S%iF_pRGFml>DRl-LwgzZF36!c7_sn%$0;{;>KV(R^a2Sm=Xv$7d<*5 zb-+QX?h*t8o&_FCR>TVMuvt-$z34bts-O$rHaCD;f!Ys$%BI~}izYKi5HVjJv5K$t zm``gi9Iud{)yR!-Y;D4v{D&je0MDE@Z4_t+3BBh`6+<}q!TucH@|fTTou&f)#VkWj znw>K|?At~<#gw0^T+|LDIYNI!U(H+p^RRFAkqhUxqdIA17P2Hh@VfX zAhn{65)JLM&&}1(E&o#-wX>5g5-il6n!4F`f8U#Ly%Y6FVz zm`hOyEJWR(u4YA&qc-4geu9-MXfiEkS*Mk{?lr&i6Dliy=T=tLPpH}Lb9z*tzckIiR+ks-=yUMW~b9G=m-sYd;nGIQ?^Oc4o0;i~m@@EL)os^;C zVMIGX8O#gIdoB&*T=K&QKa@~llIi>sx@^L&?4yF#MXIVMnU8VH*)7Y{sfr=r1~CD zk#~>L0vY|(_Kg6C&3*s(f#IYZwQb_(&)?va=yBPlLRt}WDek`+0EJg@kEysc ztI8e&R=mjr>4a$&Yq!e{X_1z|w12GhuU1HwFaxHhulxZ_`{1C~_NmvlYr!T_f0As{ z%4Ild!05xoobr=4(vl>f%!>M{ajAqO(GF~0hr$e>f`|=>-FcYkQCY7xSHCa=Ej{%Wx3Gz-hA7DYte~KRFLVPo?8& zd~;g8E8fm3`s{CnhlkaSDC1zk3dGd@(%^j?3d(uLY5OW3cAyCKH~asjvlRnp*Eb1Z z^H(ClMet{dBlKp8)?$@6T8omcp|7v4@chKO_y4TXNiXA*$*^Cplr=kX!Pit6yR z1wC{1vsvGr=$H!cX-@bX2QCT}TnWFtPAZxaEOlp^CQ%(^iz@EgT=AcHyWc(7PTGB5 z>=#TY!tAf$*5&fiDMgJF-H(Z0euri#fAxl&KJyL}_qY@eFRZL#2Y>(q?vE zbRWp^c1ODUoQrCJ+0&@5QIxdHA#hiBGO9Q+2iqR5~*2FB#x70zh0t<3J7IAp+XSM zN($>i#~dMAkD#GOuOTr>zG2L4q5%hWG9W~B3d4J-91;7So0ygLOU|MdV4Ds6CNO=4 zP6Ks9jKLl^nbI44FY&ZqX!dEbSdFUqX@j^cCT0iHS(P9?bOjCGWWco3XG-{zD*m2| zM*_W^q$hu`TONl;=2N76zgoXC)=C`)Vj^}WeC>Q5eWs0u02QT%-Z)ky>X8g(C(}~< z30hc=xD4u(zln563Kn+rsRJ>o{56Jd;mGw>&qwV-HEe%cvJ25ODLHCI1GF)R+m9PZ zc%*)`C4cB(&?jkLA8qQkM6G_RG5(~O?lA)KxWplEgxo)QxrcNKq8lI)vurfNZCPT> zoIZ;_o_##Hfo-83A9gj)sGY3eE#XzPCl2q^8-E9q1lp|N44$d}#us>0Ex|-=SCwe$ zDL{Jq-WH7@9Sb4|854@c8Hs*hyZK*On?2g=^6Sdx{kO&Fku#uq=%Rl2f44EwVj5BCvY+M;J(Xm}f-!2GsiJsHE@%t02o+mIym& zpd7C~+_cgZir&^bIu>QOmZTDmv=o&pGUKj~qsi^JHGX;|4brIzgaoA7J*i>LZbBytX7ri)h97AZNz@eibJs2oWwcqqS*VT+zddr%eq z4MU(uaNFgak138Dyp9sicx8eZI}l=XQ`GDqUA(%^jm>(GPam<)eQ?2#kZ~iRk`p?M zKPE!V>U{9Oc)p*cum6--u>J1hNX*m<1!#0XkU&1wbI}K6aymH~$P%j7npv&>U#DT_K zCwq#{qvw#(XJFe@?HQ2KLjs6_yj0xBRwr;R;VSt-=Kp}ves`e0hEv|SmXcKAXl%k! z>sBkPr8=l=-$#{E;(HVtfTu`!^S!FJZ0KLTy@?Np?v3Ug=X|UzcT^2oES&N5^`_8R zq?IVsbO<+{e7rjrKb9*esV&>zOpnh0(VOra`*xN_(ynocqeFDZV_lBdW}G?=Ud-w{ zsjy`HyA45VY?FS_v9?idlp>pWRwEQh_NtB+5GH4oh<-maNW~fPeqY1=|GXmqfpwqe zO6Q3}QJT6_2sP(DXe_c$an5XW^OAttv?EE)oKodkxyO0&rSKN-@uc$n?Hwgi4K2Q>j*K4VFqZ_`gs4_Km0R*<`t}UBbsHapDrQAJYL8u zJ7e&Rx-U>c-JaGlW~(K0v_F`sDAf9)eQbQP(q_*ZVV&`1XdWQSFa$56u5>L*fMm3Ixu;!P~PE)?)zA90Z)9aB^4=BkI_k=I)ap$%N~Jo?Rr>IK{Hgh0 zITOh|4e$j}7fwCtcoBc=>Txhb5b$_6`&|ZUZ??*~g2pXD;<-`udd@s4AP@)y8XW;o z{hp4xLN`lp+gZ--E{1zkrI6P9{U0tKG4@SB@F@8Ca~#7i$9n^1Z>h=6U{||gyg!L< zJHtB1WtyIWLCDqbe1K7>8Q_royfCn>IDiu+)Hy9LI1kp@PHq9{Qk(;lPEAcs0T5;? z{Q#WJ0$8j;{=|_X#Z1k8HVXK@ba^SSVW2@f5Z$Q-f0GznHx*VVU^U?&^t`ZZXxNbJ z7Ie7W;w!yhQIZUWnl)_&ds$pML$-smeFwA{kJMqzE>u+m&6~$z1O^;9w@){#+W`3I z>fZsRZ!B~DuS-2H@@Hp}c`H4Z+Zmm@)E>F+=Q+ZkN(J*kk8 zoW6Y%U0jVi_?Q(1g6BXqG5W{m$S*v4s}M;J{Ljio zfjj8>j|*bH2Y@2whwCG{2M~aR$W!^dTeP$iMDChKQ0yT3@l{-T?+&@RPUoMYC^^WyoCxMCN0opq!^3K=AC_xz9D~N1yI-4*biOrak^kCFwY;tviCfQo2Hmth zhO-(bk^P#d%>Fo7VSRKGX*~r;a9EBC1GD&aU2*hNy$pNM-?U3b^t0=GGB*_dpz<|d zc{VF&ne>Ik*YwqsDfaQJ0z~9{(3kn-i1hoE&`h_y>kHh7TqPWvWaX6~0otp^~MxzohejQEkQeUi~ zJ~MjOUt!!;s;%8%NLI^cyvQJc(UINw8@2m2k-Lve)i~&t&ZkIJ3j?XWU}mlxaR!EK z^ac+7>FualEQm2<`Am-!tfE5HP1P@~5z0SH0DII#YLb6C(KF=?@myPOQTwW`if8`% zy{5;F&{e>|=t!FJ7b63la=y>s4nzQlgk_5ng+)Js{FIIU%df!~H1Ngs_uT%UNfIIl zkvv!jbpMQvY^1X+I?6heW1SJJ&xM4T&Mj8Nj}&#v9DC(k=o5EeKZ^Z_JaNV+wCEiA zPRUYR3Qd~;>T^(=+pkRZe7GR~U;T0^XuS<(G~!S7I))jO_cy7vSc@)k=Y573t(>-6 zkpI9Kle4o8cf5S#;-^mIiR7N?z0jYgegRTKRnPgdAMWpOVhMzN&ihh)&L^oR>BNWw z78NJ7etiSIP$GofP~n};I+49+VaH@UD9Hsiap9Yn{$e9CbWieozS|A4T-yPnTz%Hd zY`qQ})aT)-LAfuUfT{(X&eza>oWc!P5&UXB!7F~Z9uG+;ok%R za+c4JcN1;CUOoWMGLt!P!wVf~qu{oPbZD~lt3AOIyG#^)x}3BZ#RYQmIgGrA3QkUy z!`UJ_r*0HnW?`f6s&c-|ZvI)#RS1b(&KvQaJFp?9IDEaJX%P7!lvD8ib_WsVI?~81NpsSIQxvuSLoYQPgQmfGI zR!Kk}pDHhajPDzW{Ctku{_NPhvnGz{?GyQTiR+^+94t`(v&*Suwwq+*i~#%3iCz&dXvSRe_mAL;?hW2?!zz1r99CMzas8}#L2+TfzVIiZH7Fy( zOPTr2%HW;YhlXblMcyBaH@|$I8wcFB(tkwEphE8sTO|6IIpVpYdhl4kJF;!wWwq#H z5?_YNIGr{j4d(3AHB@f*Vg;lw<~;NyHR9alfE+U`NS2UaF8xOXRYLDr)7r~OS^xo} zl2;2i*ew;ekZ#KERW9sWi8Nx_y^C~9;3LvC)n%hfZVMPw8#mA&9oKjg49N>){Prm% z;KvLFtY<8>5l(@2nHFm5^ne~bNd5B(D18(VYMUNN=QmBh6G3^s9BZS!Q>kT8FBfT* zB=?C98Lrk#q3!dP#Z-y9lfU6(X&k`q^e4onl_A2P?^eZFCfc~N89>rRjvHkFhC-)< z#KQ;RZwF7;3)_R%FMcTT+S;uB4(7HRPFKj{!+DIFUt1pY0RzI%ugeVwAg^q%JOl5O ztn)oAyk*u?d8{10qdZADUb=>#O9ajfI%K?=0tc^-2`KtA_8giT%9W5_j=&c!)tcLN z`o=DE|1HM(fKm+3asQH;iJM}>fdTf6K%|dhSlPn3m=~CW=GWkIz$|E;gPK`PR+REG zzt2*U>A36mpKAB5Zim!o``^a9E_Al#dsY`k?EnL1%Z#a98S2vwq3Snl-Kta-d0 zrWTMQB0yiXMJ|^`Kt7y48ICZBIS8?VmY;Pl#@c|c|I8x83Sx@0Ju@F4-AZ2&7Snm_ z9p)$QhVTBCVUK^pCLVNM3B?lcFT;bDIW?NjQ&XkK{-YU7o4Dp>v*a@X##I^<*bs5! zIOpT2j`|$^nl)I!n~A!NhUa_&rK_+?q4oLHz04X1qTpz@xbM-5`ChwsS`R97NN;`U zC!vpA;~&Wq@Y==UMLGJ`SIDhyEuTQirY3%8QP47L=w(PBIU0t7w^hIBG zp7kYZ|B&IWYQMMh0Va4l4CESQYIuC< zvI6s-o7bbp`;%0Rg97HojW*F)&Qo%T#~s;ge-qX^6tH7qpI4H%g(kjxn9n!+id;1; z(A(8146pBS7|~>Z6!>a>&2Env2TDUuC?;=glgm*d;6<-WAf>Y*taU@P=)NAY>{AsH zI+Ejc82XWQp6F$@%xFUohHjY7)S50@)wCF~JY9z*5L=m4`8~$8a_xH+do-OuYZg-2 zrpMsX|87p61aMSjPNhMx7FM**U|xX(SDTQ>42OZSpd2SxyW-JpK!Hvt-)}ho7c_A5p zlX$-E_8-ci-@Gvgau-SB{`UwmmDTU&fdSX!--mK2jS2yn?|uN5J2@?Jn9@@@^}*!S zxdfGj#vlI}(}yT58eZLDWi>oK?xOEfJ@-vD-J@6GD;Np*$KOmvRsA6q<7NHoQ}oo7 zem1IoA?#N-6jS8CKf)nmJVL>L#FSv**(lLAoP6`udD$6aC43>MqAM`tylmRy2;vC5 zR5nnzKJi=UA-P{ks5FY?Kxh=DsX4q2-1&Q=Qf=^QEUS_UhrhF5naAJwvmGZLy$UZ% zip-~~+~>@7INo2*)4Kde6LOeKKATE+BeytSb_ z08B*f*oJ^qlc%OW+&EnL;mvovJwrOCtQW=}{?UN>X|>%X>#Q<#uOyeTRBhK_cUJGc z?pFJgm`I)b4MRG#hwQyP3PM_F80TOFC4^yVh4e5x;f7QCfoO6hdofiq?9<9Qa|1{|~oUL%qSgDMO$cSS9 zDU#~Eil&wX21I@ywbc7Zgj#{0l;kpqV+~i)I3-+ApTp`tVcMOR-wDFZ?<<>jNO$`N z0u=NT4Q^{}@>*Xg9=dAL&_B)Xj?g{%1PsWybYlpB6~O5>+&^iB%NT{8MN>t?(x0W_ zy1S5GfR$Igja_$NQw~d2D|ZujkBxdRY>bU>W+FBr@d z;t8YJh&_c${r(+J#KQXc8%L_1yS)yGaz+{&+2jBym-GNf7(wo;(8$~{QjI0?I9}0U z1M4!yLnqaufck7iF`~jtA~-=W@P0-4Ewv>2@0E@dcX?6j zIN$7>l3~paT($Sqh(>94iE++a5S-M{dV8*b-&U5kJA5)r^y=D+9hDwoZ}b8yxz;IA z)oNH@qlgrmBkq3%b9$NgLiZ}g9mF4xJ4z{7`BCHqR>X+D`5IZ2v1s*sc{JfL2MyD6 z$F%@;4T*~t0i$z_Rbh}x(`fnj{nH5yM?GKP4C+^ttPkh&es{&L5($S*t<`oeU9mQ$ zu464aKxoUrM>A^Auv<>{9fSNHQ9|}XeP`0&6!fipl*Sde_0FUc@5E()4yIMUS4Tq) zYKHHNnrFrDREY)qqVE-iV_4qJv2p8Riwb#TW%57q*k4;sm1n0}=kEr;Cl z-1*sPKASgPYTSsxim|m+vsf7K>Gb5)Ds6y-nZIwk?v}oSKMX-R7D(k9Wb*2%Uit|k z!}me&X3rkvvQ&a-&x!;{9v_QIUtcn=f~U5(@5#yAW>b%e;dzGjG7O?Xi7FbnN0m5Q zZ6B^Pu6KKDdlD|zlyg$P3TJqnRX@!TBWv~{WGhG=4CD#2s!%$XhdNIn}apK-fp@jns&<@ zqq&X=q0FX)G_l*tQ^PpWvPaFKTz2E;4NB?ojaLU6l~;$Jh%-!`#EcStTz$DYc2q~2UCCl$DF zfamhOmjh@?M99_h)V>nv>Ep|b*Ch>LWsNKu^9Hdpmtjcg`B$r*bjW-#-W?RBs>8v^ zH_1|I&2D_*?=)1Oy<;;mgXl8A{xoG|WH=2s06QJo(On7|9PJ!V{&0cx4;aCuG@0ld zcR!&=G9dx3(}3+|BP1;Bk|2?el2d#*k_>G=iSu=Y%;iFny$v*e^tfKEZ{II84K>df|4*3O7) zTM#9fCWHyZ&qI+9nokA1Tg z*V`6y^Y%WrXh$fs>HwYk8daX7_2nza)<8qM+w@l=d}K3EK(Bp zqFGNSZSs(9y6y>D``5^6Qff>*^T%k+i`3wUIJ4B`o+9DKC7lGXS&LnB#<5h!mHU~b zPW{m(HKWJB*2Nj-I}Kh{@lKMr3&kD#toF7Rou_=`ay}O05OO%mr;3k)b+20-=BH)t zzWvCQ=qiG|V14(uat_7);pEyJJb-%DwoqViFH{>ywKrZ=&8m%Y_;>Z$1qbB2_l(5B zo=q%yASC;=5I`(2GExt<5cSNhscq{!Y5O(#2LVagWFUcT?z`BZt@>~~VP{c)s!p}z zNR}Lrt%O%35eKoK8?M<5ZX_n>eUaaG1>_pzyR}C|1QuGfD6Bl0eam`d1qTPQYMGoYEI@v#q_AQu9Loba!H9-a4GbSyOQi+9=~J-~#URA;ohTi3H; zWll%myuUTF4kbJwz=g5Q15^&3{>Jzf42nArOm+WAQVRxSB! zpQ`lFr!9d%1&$H;yQN=!&DJe%$LgFLpbWtxmA-xjJeJ`GqQyn!>(*bn`)tKcKp)as zNdf!ZJM*!vAz(9T`S(Gh5rt)SrY}>Thx{9&<7n_cyxM+}Bkxt`Z}@t*RCu><^}m8A zYb5!*95*+D82Uc=SG9psUJ2^If%}%kb1s>UFCz+bSom3azeps6@lK*QfQ7*VEzL^A z;i-q3IXAsOTHc#5gILNwBsH8mYTeyXWz_sC{B$KrAd?H;9D^0{XFH8Ce?U&FfM|Cs z2lfYvnW$n8-B@zZJDx7Lh{W2IDWRw7W`>-{LQyq&W4V%SH@U%P}gcVEa9+~VRz zRRhPq95loz95U`({La&YygmyE+*5-_TB$zqPxR?-1g81a`+C@JYj|8PewN7|Kg!dV z+&j;IF3SIIp+Q=6J!)9oj~FZ|`s|kz2-3=UZwD*L$>QC5npY0g^vlfOfBXWFj2W?w z=Seun0+9oenwWDddb*rK&xLZCl;?kdM!g5txLAyNbS2ANEWqkjN~)hjp;mULwkt5N zY@F>L$;d2%MnvL&TP%xLo#MO|>0p{+v;3=b(!P~8-=Ve6P&$NYPoLlZ<0M_Q z`7T$uDpFlC?-orq!Y`;La?)qyW=V@O(c(k~FIFZ^TcW94{#zTc@M z;Dy=uS#$qqD_u|OYg(T7kFRvT+DN}0P`(DT6paZe@Ty@C{b8ZAkv|jcDR^zLf4t*_ zw4Ag*JX#qjcXi=at9Dod+R(|t$M>g6SFgne0g4r!gOW|2lIqOo=>+FDHaZ_EpU5Mz_`y*Igq=9QmEGeplqMen07hK5!C(1w0HQN%m|r;FZ1 zW)Ir1-2uC!8JTz8h$P{kkQlU=$C#yLhx)P3k+8n)rFLF4Vj}Wmcjw*mA{{1tU7;5o zf6e%~>Lg$g2|CT}tZYw=W|QqoPk`N7W6#p*u@hp1buIfyeY`@JzBV z9xqQmt5sV;N|1xx1J}(>kbUPjlyk#*e1sgE3Ch>4-dK+U3I36cq2-&JBUnGv04{rF zzFo=AM~5b3<-BWlBzZMCk4z@frY6_<+NtG;P_%IPA^@DdW@my^Ge?)Tq%-)YLC7Cd<^>9UJg8cz*isAx-Q)&{im zB)pt3&3|Cj4ml6J9zE$TdmFvUc#m~X6ZJcdGt9k&$^1h_4(Q@xwfeadbsv3UXy)JJSYIq%>Q~KW!2xkZ*$&kmzou|z$ex# zR<*iB@QB)UmjwJQ;B{<3m7T$7uf0r@Sg=XoS(@-V7WD^urSPvmo?c^7(r6{%;~X#h znU-5jZ?9#v?MH4?h_}=^Hyjg+1~fh$hTGG zo7a8-lRRvsWIbEOLsHNxM5zvcL?5*}@ySE-M$b0;+VGH=o8NRW;xk@OR#(67rF{}c z)=H_*&Y0akkz|0PaQ$XMCr&>jgdC23AcOmY7KTtoUZTW1Tdub$7E&jRiG@%_A`L~U zp7gOdF56fbYy{FSBU_4C#cRk6>~+DNU`%==IZh#=y$d;->QtLv%1`wTa>m7xpquuF zhP3W!N%5H?)hxS{D8%cVo7t7!wn!Y+`dO1fd-|rW0&E{dFmt~m!3W+*sRndd7%xk1 zxGzU?r6Eo^=RF9Z4g5uy;yVvcTQJVVEl9IUE_Z8+V4K7-wo{_x9l6;DUu6cF6&P=T^$8K@ znH$FYEB7P6Z&CMC|926;`>X{!ivWL?W5@m>bNCUKA$!-hh}=pq^q-Vl1n|-noxB=g zx7SU5{UHMc55Q{@{6-n$N$!Qe1x9%BKeG?vs+P^iCzRjB3kSZxz%q;dKxu7~(>i&m z;aghGm0)AW#3L8{-pRniZ!xP~_LhVx|&42UmM*9JB69;F$JyF>lFelC; zyFFBA8)a4d+>i~f#WT-8R(G)Nvn+J%sfWmfB0p~5o^K)UH`Zos-25fSxo`v@vJ%&79wqFZr-=c`Zz^SUd64qhJ^d|SFewEFq0qWm9oUerx;{jG{f{8?IX$^djFk;h!SLd{Ur_8A} zB3Kb6-x0AJ{Kde9!oeY;eo;au@h?BYhtYpl#P19d)v|a^Z1rjrgRT-;1B_=LYpV=y zz6-#Yt-FKZW0}2wV;N<&|L6^h?8ih$pK^8AT&%BIA$*ORA8R+}ga?UF{-6d%Yi+FT zQ%G9+ZkG;Z!E-D2%|sth5MB{}Ti_Zsh=q;O2w z46TQwXV!7>lX!BF0cn#H>dXa2fW$p!B2N1W6kH+ckBONE2q);X#@AF!r|lR`SxF)TA*#E^dp1L~sg@v>sGwRWU(g zrA@mT8G?=Hoj~m$edv;PgN-Tms%SVD%|UDWOGy%uljn!odXBkUDd6lXY%T> za|}GKuQiMlR;>CAq<%H18JwRmF1ZV6U+XP0op<4mSUow5L|HNZ`P08Wt*r-57v?8h z++7y-IeL~iGRkH}*6~0epvxq?@k{pKolw{C3}x32Zz}I@n%)iNuR?-NsUQjJXwiXZ zd1QD!7#BJYiD3osJ&808ebBcsdM{HOn|}U1Q6~ZKJ)$1$QV6Z#LJ5#PhmsX&{e($~ zo+K7?Cvt_*6Z%vIAubJe))~oBom4Txw!FcwA`M<87dX1mba0<>$nwsA>qokwz&4~e zlXi-FI_?jqB|=SM$Rv*Gk5afT`kf$&6hil#P;1=!NLR!CjUM)&sb1JEJ_t-nxg@;v zqo+3-Ze5B}_|s@^{M(s7qZMyq2JKIYY3YSK^(`BS5DcgHuN8Xw8F0>#Aqwv(VWLl0 zSnb$wM>y3Ct}F!$ATOJ%N-Tc{z53qw^kXh}Vhz?eBe|2*R2sg(&^riasgWm~-i<2* znnqU-I69uPD?F3Uu_5e5m8wU$7&~e-4vVT3k;efT>dbP_X}_C(i6w=6SYe_`qmgH zl$E|%G=5MzIgS^3E3C-d(I}cN(CgDc+KXA$)Xzh4Y`%&oTOdJ3`Lu+G|F(Hye}oD2 z{@tq-3{%X)g_^Zp?Q@c;J3H8Z#+Y6xg~qqZ3ae>x61^3w6-2&udR_C#z>Y;sO;&`g zHGfbKuGM?-6^pd`S`Ax`?2%B$zlbb)V$-)&NqSo3`m%+i>OfS+m)}_ZF8?$+X--2e zm=uF?$)G+D7*hTsioA-`)K?gbpUljU%uEa#8VGEPt<5u0|JIFtiM95C2J^Q}|=myO59mN#ER;GPr z5`m5Zm%$*=kI1NF;1d1_&_e3Ef*Ee3LjS0fWDJ2NPK1Gfa2-&5v2<6x`@T#Acy1(8 z08x#Q0qW6`-H(bbfXqqdo6+r#SGUN309j}w_SRErJ?m_bE#OzvfX4VyS6=58gB$>? znOcgV@Jts4d^v;-f1xphfcPmVlbuA+{qK7rp!XEIM&{hAo@(H#*b)GgoC<)|i@HIs zxW5HZ*omp^9zY~>p#GQIHFRp)PLbzxS0b$2INvaKP8_^FSL`S7ABe zba92r63z4Z{|&=?7r6nzCzMUV3B#YYl<)r5I$O4EB7RJ&*tKJ4Vf*#o>a40wz^MK1 znK7oLM(PAnhcj)+q!c1zHYC((tpzZ^{ z*l9b_cCvnP3Rn566!*zet-Q;+1*E$&k)tDv{-9#Xh+XLOA~rdN@n_%i!@$uB&*hsE zM~f#d!i0@_3DZPB!DBK#|0db3GCZ%?E+IXSs+g@CiRa{_nG@=lW#>s~<0qHdNOjjg zdgj=P7<%Iw_Ek;xjCYPUP`LN8y|&$sNUlpcqsl+IQH5BMzl;j?jB9*CKke#%3AwD-}h(w6Ej&UOy+$Eq3W;N#uNe&KVsL zs@(r#I;vitOZRNJ>xqqVyoecs0bdV0(%Z+j^uth)eF zZ1;%0+!by6AfDAsjCKn|oLnz^t!Ah8ba|^GVUSwv0uro_VTqSV&(VjcdF$MHuVbJz z^loIVA;Fn?q9ZWyA@vX5irbi&D{%V;yjJ~*-dsEx_#XY*1{hJ+d&JYDIRJrRTL$nh zW&o7%Nns3d1@tR}GFW03tGBl&(*X}hjDTjXd6=t@yk^ODzA>WaR#oJnytd9*l>^B8 zwOt;}jzMqhS?lM_X!Kxb6%8xNZPyD!U86#~&V z%EkksrpHlhw!cZrSSfeA8Rv^+YMT44?f%UlGH8xNXcC4JBaFW86=%KsawVbQZJZS9 zyGk?Geik*%=gZ@BJ}4t|F5Gu3;Bp%jn*Rv#LHcZ9w;CejWzg6D+JpNg@HZAD@T&(_ z`g!bUhebuqy?W1-6yKA^zHraBtoB;5t7TQl@;fqG=YY#5)(%}Vk_r2UI(kCSnLJb1 z2lJ}MyNg*$n%WUzH>(A2Fu=)f17+A+@m^;mjg8H?f6_c)6y&IAxVHfXroq3k{0fN) z4toYK@#im>&DZKIPQ`IL$&l-X{?Qwe2ST5_Y?qqxR<{9oqLbW~W~KHHeM*mCu`m0+ zdR~RS1*_GIg)?V|2f6*7L`$YFI^-Rp?)&5HO0(k~aq6$9h zUIxPGm7)7o~W=S9bKmGDnILS|VcW1(5;sXTZ zLwQh!5c)l`Gb$MJ(0I3#75B*vld|Jk*fLK%8&l4yaUD_b^Il;Tcj*@!4aq8_wk=@4 zpQVxhYz89&uca^rmlv7p}tC-g!^&k>+2ssV974cNTzzZ!5U66C! z`?Sf+vhn%0^=41v){aBnP{8G(fK&uxjd5$UvJ10os$X1>+0#j zI$@Jo;u#aQd~XQ&ATYR0Z-M$Qn#83_50_%NDuHOmZFdNYMM*klT{RTK(z3;wocy`f_hr+8l?@=uR@e^_CK?X=f|j zV%n0h-VHGxo!UCXN6yD~T)mpSw^$EIy}s&!SA6${AdPd|?kNScL4TJU<}!D24vWQcsk`fG93U?3odjmiDpJ=Ve4 zcSB_t>;nB3VKcKKKpzA6yH&9|q}y*jlN~yM9(~8v%c!l5mb=}u_NP!>J1O3~qcxGS z6J+7Kd8;f{VfVk(0IOJ4XE&|%zMXUaFZd$YnxAj9NEl}EC0@@ZEVu_xP1JPvEO47Q z^Q_Jt#A}J2R-BUwU8kWu+5%)n_KH6kGW*EgR0^Ujf;uJXsyJg35cz?aCrWFxlsthS zwL=y7NCWgOv^`<}IZ30M079eyCH*w+h#~|u04UF>0ac;uhhre50=)0X`3Ua8Z-RG6 z2@;n1GVDZkACHLa7#C*SzuimkAScfoE>z529&; zS?-w#o9DA#wS+}*_#=uiz2SL>nZxo&bRQYR=1v$C5{ct z+5lq9XkXb>WbktM4qX{Ib`%I|+QTA1GtUgh#^UJz~ZS zz~0HQ%4F8_-{I?EGY#Py;tm_eP~;u56|E&TUKJNxKcLzIW#i|MO{iICx_l{ryS0}@ zHT2#8xbD{e-HKk6mA=S{+a@x};~IguZI%{;i- zu~FJC0Is@5*L<}du2j^dg>QT9Xf}FcCey}K!sBP_)frLEVc~N4sP84V%XL?u2_s@mO zj1CIy@bl#~_w~?;n=orrk4(y z!pNQ1c3Nj4A9eE7!L_O%6mD|&GS=-BeGN=;eH1FF% zdHqlWk7+G+L65Qwo5<*RyG*^59pK2gjVTp@Xno*EXbVaCjjoJk9&zUy4Vlr?1LwdT zOW+BR9sd_~Zy6S4*Y=O9bf+K<0s}}20|L_B-3<;65(0vBNlEw6Ln9?E(x7ygfKt+- zgmkzU&wKyhzt2AIulvLPML#H z#a|7@63$saC}@&6fpn3DLP+cQ~yb5WyxT5Y-?+sdcSDH?(|^O_E7Ko8j7ohILdyrpRcEs zmuo;BW$4m~71{NJ)XC>SKj}}P^{p4lAvE*#bMv^u=YAbyuq5MMDM_sn9K04ICgp7J z-8VziI&b0Tq)B5|n7;JC1um70j)a0eFYAs$ATza?>ew&`VW01Edr+KfwUeTPVXZDm zg>TqofCp<+RMkZ9Zc$IVeloVK`&NO4RdZc9=^`bOu+udDjC_|j^<_8NEUewT|J}*{WL3*^ zjxyC@sl?AD#-?tQL@6Ghf07bI{u~(+zn<)_lCcyL-AQB?^5Q;d^KY{@Rn0UaZtaek z)hA;zy%kgUJVFrr0SHxhDrQlVTGcALGFi+(Fh>9AbE7DU;LpY0i7Z!X4^gGTDjmKV z(MPhd2$eVka$^$>HKLAkSm?P^5yy*urPO!J7tH$dsHDd4b(*Rr&RE)C-#GaTM*dhJ zyIc+)Z6N~CYyZ_vVP@-_?=W{akzCW2s&{y6JoKV5pmI7z4zczU2u7rn899!olvA8z zvrf0)FOB&7As=m2oj!xD7Ua-hSGHVzM10y3_H6pR4YLYtGx^Uh3bPx?=W9J(Y*Hwz zud^c{^7T&q(W{oGLTEtlH<>`VN#!LVTZ7%MA{(Ip|+T|*$jun zhtDMHVS$-FG|iP4tXs`e^qA^Je?&oU2r1tuuA6q6P+2ab^2NJ>Mcd*mOuJ@^$i{rT zU+<)-0fYJ!3I8#jV3weJZ8ZOQ%T_)vOn4*s+Wa0 zuqgo~#dT?Z#Cce`uyzmiwdMxAL_6}GdV@F|5U>QrkJUM7xS!5;n^87a)uRHrS zxF(nC0}D#}j5tQ)nO}@IhXF{q;g>Jw*Sri?+L=`{Xk0AsBaT#1Q=C8jxcqK?hQ6V6 zgmNJ-zatZ7WaVN=ll%irh8!%+!G@3qRQXG8Z5*`@vZ z3r{PtS%c7&D3FLOvwtl)`{rk&tf}WxLT}cC?-%FH?hOawp;guA4<>d4h6{}|K3X1e zwu2Dp9d9wsk7+j?>qmWFVQ%rmv)9(j_S8ffw!>ujY2SEPaXJM$;Z!8`R~tEmE&_fX zH%C41X%r0I)tr|?uhJ*uq%^|3lp7la zPsuNSA4k;qT=CiQ)x96$@BW>VqpOJ~*W7UW)LRC*D1GE@yQ{2}rf00j3r8Fp?`kgb!@2Ps0gEHsqtv4-A zJS+p1h1+cF?A09)To=7I=bCM2EG*uJ03Lu!o1(3Khn`ad+6N8uPZ9kHtf(2&=m9vHB zCbaCDb9$jEA|GxJgNHJcdAm0$>k7-8_qt9+#$M~OdSwevb`71R)Shu^PTO79+3^mW zr^PHd7%!@Sa!Z-6YeO*@F5QSG8iwhh4Ib_O@;CTH<@4t3aj)q;m!MP2q1W;I&-BD` zC(k~uQ`V*jvu7fV+>VJ5ubjpke?E0b4XLfCEhh!l9Pg-jPz24apul6nh?g&@D-Y$xm%6Sg)N$IP zL-!CvqL*k(%$Jxd)6M0r;A((^rTb*>)A(s{wzE{nXPf$WqO@x zZcvWFi(EAhYnt-v=7Z^+aGXfnm-yYcw~hrdY!S1<@BKzc)xw;9YcI-PE)(sn{j6i9 zsae9G`DnpM@r)^UbJX=n_WShh+a&I+LZ3^4k={6*x|{Pf^g(S;nhW-}jm6FN8`bud z!I)j0wRfc=H^FaYVs8xGf>JbJg?!=1+O#+mIZ1J-|LHp6kTRi2rI+FXWOs&sl+vqQ z+~~is+fcK;Hps9Pc(T?TpYUhyefwCu>>OiB^6usW$8M%vpLrzq;bX4p2*`dn0TQ0D zvS&56(4*_x(|Z_Fi6^hWe|r7QA~oe%$cG}ESm9IjR9-qb@!Md!`L25bXXDcANj*0t8`cL9~}0+qzMz}$HnZTO+0BWWbo8t zQ2sV9T`EM!ahp%}8nZ_bE7Mx&__CL-wBq%6x|d;lui#kQj+g6h_Ut^~)Sn0ALI*7i z!;`E5T+X)bYW7qCw~oQol*b1zgw^^D(HH|=E|f4X8idj2#X_8Ca8M{YMcXpsXIhY` zc%vEu1=q#wbWNHap&jPK^NYs1M5s&HJrm(YaQ;CIg~;G#He{!?+GiR1Z%yjjhboXD zu*^zvLG{U%6+-i|Ssh^kzndGF<5kixEHC}khQl321NeEn0iiPF%Q?Y%Z<_mYIi2;s zW!ixuaFksS!s6hv#ysS6_Uj>#l|nLU`(FNI%^Pj5=RQ)RVBoti5s+V>;Oz?x6fs=~ z4-{MRdHbllVug`@($>?$liWcTMQ*9hmwppArD}Mn)mK3CfLaNE^@b09$#;58=1h^_ zQykuj>9mXOtjAegyD5YEM4vu8%tnK$pklT|7U9ReP|ibkm~jBiH6Ag9JE8tks`n+| zwA_485ACzUM-9OC2p&TP_3N0rB`AMw1~&hUa(^w}<=uHAGvD60+tT7yvJLV+9v;q0Ji7KCV z=+uW@`q_Td2_Yb8=VoJ@MFg%tB&(!6#V+ZTgzWtm>b(2LKb13iJA5pgjz7DiAb0CS zc`ef({kxv88It2fzw5l2@hLR(!yl{o*_kg2(DdAMvv0F}>EWr1qivUTm(doF*SqK& z2_We|5>jmvis4Ifu2Mvq(;Cc2MUpR!8L>Fy$Cq~{^)syuX#jXh8!`z<*Jd`E0VzuxhX zKBFM|Q5Kx5(@2N4`V{%G{9LAjza(C}l!nUiB*`+Zp{;=AevHU@PhKHxTEJ4c%KcPomlUoMgrwXRY5OQFV@PN*e{`Em?Vk~wFqlW6626H#n~&1Xi^ zq8T0@R*n z)Zw}Wkicmy5hyL0v(MUBeuOV3a)|us^{TWV>KH#oBwnt)G}M+}iX2TQiLA=I6NQx6 zc!AxaHzc*Ifa_qa@X-db{IER^6FOvp_t4n<9#Y`OSp^*wo+liaEmHPbb5i1pk6;8Q z9d?Q(cc!e{(2wC860fyTJPt8I7;eCleB(vV#Zmg#KPhdE zEwmSG8KsUZVntJHalcB;Og3b_`p)IGGORj`#2aEgjDpj(kS z9_ecBRIuMQTQiEF&cqW%HZkI$!cwvPrtxRN=<8c%MaCsl?!)~(|0^B^HyN25oSObK z(h5N$kLJwJm;Nsp;rtb!t|HW3bL`d*RwKuBbxq1DZajt-Ts^R&{=5&dNvLXTr&|xV zvLQ+&)Gggeh{!fG{_I^1`{Y$PXfQZ{yOW8fmp&Mjh{Iqal>zet)nV4fFrQ2CU)7n7TXIk*}hU4hOcPK+RgzKPEqd|w z5cpntJUxjuSL?Erv$4O*jGk6&#H0%ET)hiF+4C zhpD>fmGQAOR%~cb9-@A?(bA9Ps&9`$8hsf7J9_VYgeAb`5ZQf(yU4k4pgWTqhXv_1 zlpTj1->mJvS8I8SJ>EPKWPq%e>tnq2-T?qE7iJ`EO?C&Tb)RHgcQrr@>r z&B=b&)sQ~clWm&4Kf>F^OG7mvhKVTjYT-OtN4+Nbka*wF+)}w_qt#!P>8OfG*&jV7 zfPNXB|A8h=AR9v9#Bh~Y@B#RUYo9(&8cMN{)%tvcRlV1EhHYlKhGh0LfekZqrVSr) zjY;Ky{w(^@eI|JH`HJTx<$YN*xTKi@Ej%r=_IEHNtsMNg+!oi<$vI!9Bf=`*PiT=} zwu8VKnH&}T2~P0))E)4(e=^XaR)~va_@^IIds(sVb-cYod4(kDx-WHpO?jmhT|0@W zx9=LzE`yXH^Bc1NG*;;a`km&aL7UjV$C-b16+8xfuI7*YR_J$`k>6~sPKPk*UdHxN z#VzlY=w{zp@1RNR%Z3MfVGFhw((Au(o;i-W=bt!LY&s~Q^N%|ys80U+3>pS{aM!;7 z{3AyjFj>?u`lr&{qmgZ!pDlivlPa(b-x;C*&?kHD|NPe9e?uD)Ks3zo*l+)TnsYa% z{|xP2;~qXxfxZtK<$3bTm&&DZ*-_!|##266nKDqz^Fbrtl zQ>96bwVHwUbbp~ybw+dZ@{WgKo$V!a2ln5oe%F8=BoUYqkKE1xK6JYKjQ;MLJnA(_ zUAqFmAp^hpz&iU`l{+ZL(6K&w2s)`mlCnu-d9pjJ+ID@UsQ)6G7Y2&E3?^0}povVX zDaF}u2IjAT{!lTBgdGNSG>xYoJ5wyW`gfYUJEXAV50E+XW;ShV>l$kV%YM!Dm(AO* z$%#zr?DNloXUARAe;ojiDS+uY2=qtxqc*@nF*RAopj3s2+UG$04lUdX*~HNUoQ`rl z$Yk&t&>1;=1u4?anzaN)15-nQY*#4v|L(WHWWDhPB4`iJ-=vq%*YAqZa4$I2GS+Ij z8AqMbnXCYu`Uw!mmfaytF+dLOsnt4?{4+1QA2*yC!QH`c+(M zems5C^7jIjM}7iG|ue@>u+u8T*nq)TVYaw9C} zC6ZeK5(-BAD(IX;?QnT(?GU%3L0u>?%{9Jb1Y7{iNX<#M`Y8=pfW?xN{zV>;17=e znM{@d>96xcRv%3OU6OrsvcpK@cW!P+*hhKe3*>=H(VV1uV92NL|7`U>O94aO9YkW{ zX`Z*F#hT;=;NfKUjm%dg1ljiZ*?+*2CFXcV^LoMYnZQvW{j1{9jJOEuHes`D;x=Ph zN|z9~UyH(I4~=ILqSB#-nNx^sgVW+9$eXyo$M_?DNtz9{|Mela&$%OFvQEj`uw2xW7e`fR~TRQx^iQ^}}EtB75Ib}NJLav4Ml{0N|H%eIS+_nI>3 z0q0a;(ahZ9>)0bMfHi0zz$961&}}*4vUQdCGIg&xTYo2Lw*uql3>|l%v&2-6i0OWd zlL%1F@k%;b8;9|d8<6%a%F4qHsqPF$+nhi*e4FH7Z}SW69mZ=q8=OuzkN~W?1!YlG z({xBQq`m=q52dXIopetz2Du<~$)>C}oNj#|Q#e6NYbFd-Sb2I^ zZVC35%&OYW29hXo4a#vjY;UQ8(W$b{8*gd{JCPeyW+Hp`GBf#p-ug$K$Mv2x62AD@ zKS|jw;tU9efnNCQr_j^PGht;CQy-VRtbA16Cp}gpk2%W(nWIF@YXC}Ft%#l9ktNE~ zRW?f3-t@)MftnMkdWK`=PIcolr{B#qLPf%6s8+lsZsbd){S{Kk+AF0m>i1L+8!kTn zJ*5xnbCEi}Aq*0_z*su?18B}m98E^r%RSc+6w4E4GwFY^0(dd(k(;YSJfn43A zzups7!3TmsZMJ-aXu(6U9%Rjd(5%rj2Dk+2XF(MP*s>eSNIt0epsQ&Zs1d0segXtA zlBvCk5wNxQ;tFtE4wDsI> z$JW;K7D)0Oh2}@JYaI{mE9!N`F)Xt@rw?RDQ)`|i+bxBu(fGnrjXffM>M}X+Wwz;J z&6x$`lnXI_sAuM=$F5ha7S#G=I))KC1wFx8(+@i{WWvQ1q|?z#O86FJr$&FRb*xW3 zD$RbWnI-Y!em!%$&JD{9o2dI=XK}6xI@hNh!_!4?CyH>FA;M`hsbJn|T4Kh@`b-pK zG|NNakL3plTB~5e#0&&@ev#%4P&XuuthNP#fFKNCGrRH`KxpI?kzDe=$3<(b zF)zlnYyv+Oxha!rWNRzUS0ajN{n`937V3h$jP?9|8I_!@kyxboQpS^3+Y0f!aU#2V zQKyT5Y&0(XUWV&^nFMW&b*77q72H!wE+@=}8>6Nh_qpD4T9SVGc(qEqT1=(LgKuCD zO#uE^X;hQOjW+Ma_{~J`u>$)d+K$Gs9rWTs`sxkFO*@IyF(5WY{8Z(&@^Y#|xHW$f zglDR;h8TH)SLdGxM4WMZt_Vo02cD0A0t3&QHqoyjJ0AA$pj-`E(7xXLTP-b{Jp{@6 zY?^qjAmaYa-p^7nrzfW!~@~wxp?Mfth!jGU%`u&1d8&k4As|YxbtRp#> znV5PF@Sym*I_1^?Aj12d1sul3Y3~Nyjo+Vw)_t78qDdI4nF=Co)$r(3e%a!6B zzbE=KAXutbva#VB4K^zwoh`{UAX2W24J_FbtS`wfPt5X|@1KxV`4L1$2XA!U2tlKC z4p`r8+o7+w9!|M8!AI2MxV`?&m?x+dV}18ND93+M2ADZLNt4;@Rv;%K5((1^N~GEdt&y0Oq; z;sD!=*xbf4Yw?IZ4~!?bw}V5H)YeVXYsY1Fwc9(&_H}kdY+U=d zr!DW7(Fg`v^V(AW%M}#bb9-;nmVb`>Jt0(nM z>nEV&Mtx2aso))-x}neamV5Oe$szhp&%24<`lf!jetS7X=dKXRG=@3(qS59`OYf%{ z8y7258~scNFWK(aSFq@p8hTZ7lpA~IR6MgSKfWctluo)L zgA5jNA}3zA5aH&%Ymp)v#GD_z%HrE59>9{lWZ3?Zf&%tO^B+fo89g23(tk#Re12Q@ z?+d}vV=gKTRuAorcyY9f8bBeOCm{wT-NLPVoC*T2YjWz7&p+uLSuebjif3VRpdvV} zeG#iTFwDB=)u+R{7uD+*yh9rht;GlMUBq!QRzyK!T{LNye^n_ts`*--{~k(fwLu881yj!25z^x^3PA zB4pao+&}NcJEzy+rhaUa4qh^j6qzueCLL}%h44Ln$d$QVqR`HzjCz;-LJq^3FKiI~ z)C2&T4H7FNUl*n7&pezQ7P@ZZ9nKPoPqVdnoUwGO>U3~Pac)FPF6zc)2OOQcQpD<_ zyfeKv(xrvPvAoAnSB~%TIb;!r>|1)OuIAu2V2#&*tm(pJ4S{`(ak%Q9c`T&4NZ>ro z>Yy$8>F38y>IFe2)*KbSJ`nk{fufD=TQCKk8s^N`;WvC4&zv{}-+4Tp>VELN*?vvRkow62Ej2CS7mq(SMs(wvDB38Cws5$KjoZN@+nJH5a^<$~dV>W5^kL0-~1@y|53?%~aYIDDa&dx{_ipC}v8n+y=4 z%8KNsq{houFEmvJ%yU#vwJfWYr^_EQ&hpyxnRmA3#sw9K1xypYA`p)%zK?dQrX8J2 zfVKKRYt|OaJG3Te*L9FRRgeWK?=4KSnr^aoROAzZ10;o3@vd~$%0!VeMPLcke&(IU zVB|}Gm~To52&kppvWNO$(Smyls0%Qp0-VR0d>^}{NE4M;=0Gv^%_>b5Zn3ODj6??X z5F&2F_qWI?N{A0jtXDOU>$j>vpk{kBO}_Kz?~F#ty+Am=avg=ueK+6OJE&SFAYql z^}5_}3=6b>2y(C+)P?VqYkN;e#BBNbA+8=6Xe)BI*2Uk4D#Vc+ce6A*Mt^1pSFs1B zFXw73Tl7mey?EhDZ$cul<=(=Pic(1Nw490&F=kT3;?l$B`OLgtDZo=#W-4j!l8=1i zbble^G*%>1C*50OvR?|*aflwQaxDqyab2Spd0noZtAqU1hxlCaA^Mj0N*xD#<5pG# zS`aLNCua#slN7Fa-y7GOG`(}Ai)Ma*VW$qS!@3;Ij}@^aq7`Dgd{uF&Y2XXS80UM= z#QeVb-*HEh)V;|$J7gx}PBP5hDYuS91ImT;>Yowq1>MPY*mgT4g{p>EDMQ_-wC2Mk zCc5qiDM}irg1^Xz!97CO5$lXN#*BcWEeA#&CgaJ6v(6yA{+4?(rp%IctApRQGSMit zEckcHH#I;0FI2lzWvz$xMP|v6y)g2FpTL>TAVJXFoXW6e*q!jk|DJ7AZ)RE@>7kSo`*+N@6?A9tA@Mb2=1vj#W4{43Q#Sv{_~?j&Lyl=(CT zF)cYE+W?&|OI&6!~0U zK$5MSFTa|LGWddn?Ac_D{pi)nE|Wq~`A_i5A5l`=e|#A0twZQwFEm|Sv!%lqLF~>+ zaparZP+B@((3XoLl^FA-J+qpR^p)|@?zmM^dRq!BJ($C$qFZpI8kx#`dl^*fX!OZA z>Xvvw^|_9rpX6C_9iMmF->)}D0%mzhMJdE*bZX3TR8Y0ATxy3!-EPDV9@I3$$*Sq| zqJj3k8!`axb<;iJ&uIRU+lp5K5N zy;12DqSbS@(C>D2VWFU&u?~{qUfTp+mV$boayA=O=5nrsi$5nh zVu-b|lz&ge-d{4b`xe|JEj2as<+D0#?X$09&*H)TDHNjXgFV0Dx{y&~>-5`a{5qLX z2;+Tvd10)5qy2b}!n9%35k-w?vfZcNzvn*gtypWrvq0F+8z{v)ac=FaG#!tuYO#Vx zv}Lc71P>1b?!w4{-hy|RV2(eDanA+i8Kv7=_H^4RL!U0~1K9k#tU#hfgCP0|&lWa9 zMGdp8l&v}}&kjSdLuE*FJt?Zf`cg;fB0M~ zv~rqNzwS+F^n9nzQ}HQ`JLRc}tnBtw<`g$TnSV7({y$Ejoq&fV~^XegxJ z!x$AB^=mF1z#MJNlu6{S_Q*AHztm#pCBU!yYJ;{-A#OLGBcX5GYml@Qcf(RKOug~< zHG%~hRDXz5`Yzam>flgQxj>%ug!)>KuD)E>NW9a?VO5Ga48E`k(>LO-J|Fl=5 z7${U9_m&n;#H7NurGx4r+I(AOb3i^nRCLn0hf^V?;f7PV9;O#dBPs&YYt6Ez(638? zF|9GVhEXo9*s=2$+Fxj@ijoyO3dhr5d$M zoE_q1jd6@J4}55OjKN>^%H<&s+%;7S&)a zF#A}@X2N*{)H6?m5lGM4oOVKYzjG)UZ6E+oZ#OpITE1P5=p=!%P`o=W$?l41JrGZ? zSoz&FNN)Wu*0|w~i1@E$bX=&|OKUOyTLEA@n$OPaoECUYLYZf?Qo1WfYUJG6_f$o$ zBZbo86(acM!pt%da*$Ga&d(6=pLFI$SLWBOIi%l3oo~xMdI(~15ss!^GZlsl0x{O- zibR>j+qpV&hAKybNKbTCxJBAF*MTDkYncE~FZW&rI<*5%=LN{GuK8nY5)C4b1U1%`Rg38}KeZRtLP`{G8<` z)L!o2txeSxp>Jp(W__)`E~}`!;qKO9TOLk<3h_{?ulPjVyfCb{d-#xOsip|9>JYn$ z`TQznzBG}TQO3*{wKbp;(25P`te%_OfVbo)kis=nas65b3q&EQ_FS~*%8w2?4P!l+eR>o?j04eQE{@Vh=g!1Z~YP zzWVDhIXReWX2!UT?i~)?<^pp^&L?H_w>{KwK@6Cq3E;A>-PxfnVmv8p@EIs+5KZ2# znp*h>Qu-ehg&NtFNVz2)g0|}QM2$(@{ZuZ*jB8m(8KwmaQald5%fCOkf0vj>c&a=i ze!V^M85Jf?V9D|5nj1X^E1SdJ(L|^(ocw;A<~+U>?<<5=(n^hqEP6P+LFVjHyD4T! zrWpU6F1Io;)Zp8mELKg*f%bDBeCs!f*~8GU&{{B=Mkl);C8`eje!?}E3%_x;m0f&Toda_I^Upi)O3zYcKM}ewJBIA1>J0@9;Ygq?%GecDF6`=VpII0 zzpaA-@0a<(*GvaV-WwK`Rc6xB`9pb(t}>RKYdP?Vs(+S7)SuUXr@NpJK1M(m(zv+F zwA^rtZN(BUrj$leKiEW#713e`r5e5zn6s~H-LW|dSH+FQ7^KH1{%%;qZ0kCvOPt;m zfSm=TpAlmNoa20A%U^{=;`SOG7v@_L0#AzFr6&hdC~PdN(dCj{rL4GW}Ys8julQ=s{Ue zA*}ml2YE@(d`UZt9%2u&%89_se4;^9-mXKg+k+gy1;o4$`IMmxdIj>ni(zUijK5HP zL-)gj0#HW9g*)MIjf7EV(;q#k1>y;G_|BM9zp80)T3(?;F??5puwmp*!Wum_Yr$Qh zp~mIj`&&>^PBy2TINeWosVzhMb_#b>Ut;3oXHxSojs3DZ(Rk{ve!ov%m!^g5 z#r5?Qw_3KBb%Me}f2U()#GyA&|AtNf7a@gG-J#A|*4wZD-kSddIL3(Oi`@atp4f}% ze_!bTIi%sZ03yv8sr~)j<*()T573*deupfp3!yrN{2ibDGlI%!0IgKs)W6L7mqv{- z12lM!nL7Ac*I#}32f+<23I>2=(J|YN%HOfVKbaRB;yc)xIJT2w<*x(w|MX^39Q0<= zE5(oL?}_}U1yIbpfr#u~j{ZA~^M87?eb<|9A;os0e;EjJ6fh7JpI;ov{9A9_?t0^v zUAXjL_wavO5d8oQMD*%YPx^oBjo@8x1V<-+_58~~$OM9ckU4zRLhx_B`TyUvcKkse zzXnIf8pw0h6*mBMa{!1dzq(~FL;aY5Ib#elCW`G`O(7dVbSBTgL$fwwsh;aVl;I2j z?u)8(T58+=miGS|lvJ|X;9K%X&;cpq>chK+<--3e-o8A1@pZKmKOT@); zFx~)w=-5Dzs^n{Mmx;*qn76F~PQJxoo^5mhNDThXCVHypu2kh`ZViU%Jm;;12j(+p7hhhdDmnyxVu-ocG>`FiKGTgc}rADl9q_ z`+HN9v)(aEJh^SG@BG5`0r2jiW+1Kpr&hmM8xcXWGbV}mP!5%R2vjgwP*U>0SSOu~ z5!dp&+-6F5YP+}#dQt>7(q6#dd;xl$y4biXu;}=korauA4IQ3#Z-f7Fv2#grq8s$z zRY~9jigA1`w|7L!eU!dT=qKtV2AbLTA8gzaD(_V8boyC8?s6pX-@una9n3JCxy^bs zo7oFaWKUt|%tf=-|IIz$ zRN~~Bi2jF#^w0Z$EbWeX^LbLtFgPNUX!tUipPf*7CdoqM0?48>8VI8W=8K!|Y_;#| z0Zgtr)eiFkn7+6tpW?=gSLztZ3%S#8a)Q(WK)bi=A;1t zpN7w7-0T+5*>>J_R0$Nh>YmqYpfYa4oqzsNAVzP^{nhj^8^oc|X6CDM{yKP?KGXV? zrfM=kwfkn05*KH=mw{GQ zOoGok%^Xz#VA-J&Of&TxbnpUD$vv%_@8P~B9J$tHb#-4+areKHU{Jn063?O3SV=X< zV1(6;cqT~%1o=K9K#Z`4a;^u1bi~A&7+N9c8?gZZ%$E$;Ku5{j6<)+$HRqhxbe~?1 zX6*@wR|LfCEgsEFtES8!YiLAg1-a=aPOT?HJp#9srk*P(JW(UAAyB}n;VBL!z5|7= z9uGZRj+4ZV2MVgEq~kC|5JnW+u!9ZjMx9y21;=R5-~6sXTa-ZmWE<`mqh^$&iTuCB zxCCIiVJU&`MuTj%?{H9`2C_1yEkAAY0jTuw`}Mn!$BX3*@Mk!;-Ba#wf*0e zxrH`>`KrbgBe9w-DIaAmH}3~%FqjLk#%`OQ8AB=YOA4w_cfhozf;_LP$jxufylDQ& zP9@ILR{0I(Pgc%DxuiUj^J1S+amX1!?^4ni@+!>lQ6eYGP{uB+C-UW9X|n42|a};z8swG*RH=?o(>LiSL`h`R$-HI zOIc;P>+G5q&f~{+*FFId&FNfk1F6@K=x1!A+3MR&-<#c8-&6`wpi}+Ag{4?4!3*g8=O)eSsVLsSs!uO|jj<3~7ZkLoK zqtq*K{yf85T=t#T)(tge6O@3tB!q}@D^@&YAJ`)OJHCKEK)%&R^xq?8dV+3c?I7$5 zn8lB5!(VjY!+%UV@#P19*mZRqrfUK*>H>H%mshwy=J?o(5iG5A6_jqwEs7*QACgZP zB1oH47}6<1UQQdQN!bPb-jqf$crX;;ymO!VXzv!H2Z` z&^aJ`+;hD?KkR~Ya;YZb@O|ybgfq0daew_R#BU#kh0b&?CYab2km+s3O~;bPcxRv* zM7mnHue|Du1Y85b48(Uo9FWvRRlny1eTP3@P+f`BMcOAO)YdkNqC`stI9&~G0oHQ` zA>I~QG_Vy!6o&$&zXf-V!pP+%(Q6GN?iGS$`4DhhnwcSR<#f4EBu7JD3+=?fC70~e z(MWSMO9%toqr!w+wdoGpcO*nnrupSY>qOV~Enc2(D4w9<69$o*(OtqM$BPNSjUrAg z#63RH$UtnwpZ{mzIof@v+to85>AtH^wGNmcNkFaM59vyCQ^1@dG^ct(L=tCA`uAy7 z^bnaGXS_%1U4;ZccYsv@R$xd0*Ov6e42$pYaakF?11MPolAd|!!*@Qp+kt&O{iG&b z-M2Y5DqN-XoaWdGz2RDL zZllxEB2?TauG=?}M1o*RmPZ9T=l@V5WaIUEvMygV&l=Y7F*wjz0o+mT7gS5u(4I>p z_IuojZqK&ALxg2oh_nwg1ij{dcHD;ZA2U_c(DNE=Rv3k&xe1U0U2RZrbH<5gqwyaP zbx>B2TjgaQ`^-H*0@4J&3O>(3EcghQJmF5UDO#=8XE5+yO-#XJ#FZC< z8h?g1q0+7>=!3c4{ech>fr5I2y3gVp+!lVeN^7%`Ze-4lgu|EZX#DpjAgu_0EYS6( z`X&U0%6Tpa?yxb^_=VhFkDAe4+RLqjGP!uICbuV_IU8%5p9CZaeHDv$;h2Kq>;*{u1gFVuK`^dYRDsNT|d~4nUutw@AyLLs%`rO><}mw>~U$ zT<6wqlI5+ab1SQmSI#GwjM9}b9P4~z2d@)jBu`OAJ45-d)s~C2SqDR5EJ#s_un_N; z%7T8&fTJ70*Lc((8>w~asU>y`Ubw}*^x7h7e))mfgx6)_mXO z(W{tLMhwy^rzxA5vG_gq=XoP_5yoTku?VQ(3jX&-JE5r6qyQ2(i>_5K zfFc#c*xqb1i&h;BAuW~(k7iUbXQ_BfS(a?SR?oBx%jf@&TFA`dmzSuD!nm8F!$gCb zln&X!n4Kv3=tW!snMY}3{3cs7DjmVCqP1&#ab)r3)nq^Bpro&0yoTDoeTcB5YFjLs zws-a4H{594`%vzWg>(@`b{sql1mTZ@pBghgDvZK&pnIr~fH&H3rSfhm4Zbf5Ru^$U zQfC6z52YDj49UuP%MwMA#cpYze4`1zZXpbZTqJ}%PRg?fi~u!u3-roYTI>~ttkUqr zblMxhIXY=7DHlxp0!RQ{D6`Sh|r`8+8pAYpmY73dQ}n6xG{lM7Tl zeO3c+wM;Rj@FXZxHKY;mxakpnwPTJdtJ1_-ubc|T-P_eYw|JCV7?|o2Qe;fP zGe$BDm)!x}4aRveZo=~6J0!lpxXVt={(fnZGd_s^;&uumaVQgm?T*<@q&Yminuca0 z0ezR+r7yUrRmMPUj|Sp%b9qu5w?;m!S0h-Q_BGT!F>S;`f`U}(%FWEKsI5F*5fj%T zDLh@@-Zfg6o4qKOe=7nAIFyeB(?(YJP!pG-#m~~xR;y9NpblcO`c81OjR-P`ird#u zoKU85xij{|M z)S=&?X=)aL)p`4Nl6pw06Z=?aNa?BmXG%isr;L`vtVL=3^#iQ;6Pt)KJQvReWoB$F zo@5gbi9Dq;1){p)B)*;(DisDA_N2a$2z@zSnyOmVgT?Y{ z1tsgb;N*REz2GQ$;j?#M0wdYZdLd*&ifH(YIG1l`Y6t9M!~Q0CA2>bcQpO*yR>8@2 z?jt~r1x(eL)7%$-n#*~$z$D_Mq7x(I%P&06qa5$4b)YTAhS7QJbGxic(L<(s??Gdc z5py69Xd`UFO#`Ovy6eqQH6L&KTl*Q$xApdMBhU%*(Ov`XX)So-!=F4q4zL+y^^uOrmgh_OMeSS(=IsX1ib|L|E~)@Vl( zIyXTdXOWqYTp{1`ZB2k&Le7x7UlL09oL?+(PmE&#qX37AL4F@&UZ+l&KgzrVuQc2~ zZdHn>GLWP3vox)?W}=PGmC;y?YphKUt*?H8kO`hiA~?*KTn`L=Itu8*t(Yk|yOpM3 z@}7-PP6VT@GBB`O2;R@x=^Uo7C`BR{tNnI@JPGQFfZ%2ak=j9nS`2NHw%he3=2jNO zm)2ujl)8{t4Da>l{|S2?Ak2AaXRLArRFj`ONaYt_)WmR^i_zrY6hhu7c-{Q1I|%eA z0?H(aRdJffV{BA9v~>m*NaB2jrx6RH6ju>>v`o(D{R25{h?S?5^k5QWA$ZjtDv;HA zc^()6*kH(zIe4bJUdlHdVpzOf`Y^Bs(0Hu$mCJQKsS#v#e#xGA(a%sTIIO(!oslb; zLmoPnU9ZeNMhHT#C&Z{3R(YcIez9vu5lT0;$AcLt`CMHXZ!gqehF;7q>?N$Q-`^KF!>=9Z)d0UM5*Gp+7h^!RSN0rfnd@p z+U95GS`BuzlFnKAxSsiLF1^sQ&UZo~JC|KSFKyM5Qn@7;To4IKt|I|j&o$6vjNQ!A zhfGe~oly%y7;4J{L>Kj4(ljELA4x| z>JkG~S**qMeUV0srh=kz z2j5XRpd2+>U!ao;AC?*Ce~IvGkzyr??mEscISE?l+;r!={jP9qMOB5zq;sE$@!*hI z^uM4Cb$YeMTq*$DU-e<=0fn6!N;M7u&XE{ zBxH?b21l<;8DF3&$JnnNeWiZv*qv0ttRPRz@%xX4NqnLuvJ!ca%}!g= zjEB&tGJWrwovr3JfS_(+tx$B5@Ogzagjh?MxU*?78CHZB*%e}ynK$rpdy%YxyK7^$ ztJKp1RqA7~p}sv3DQq)LqFEH5nC%UAsfqqCty9O81W01a=YQRx$4>Yv>Acn#U+PFD z>esT*`e*`|jJrUknLoV=*f8z(5Z#-bJb~#HSGuq&NK)DClt@x-5q}QF;=N$oYW`3% zEFOH%7T0UKj*o#;@XJDK(KyUhZO1Aglkt$9W=zTwHs*zTw%j?QOEj) zNq?a5#KmG6@c+b{h=*YNbZ5VlNA%B7uFSnZn-rWQ+uJ4YWFDS^z(L@Bin6{q=9Byw zbB92nS#&1~>jqw3=crGfU{mv5){_Hdcw(Nh2sG?7%+Zmrgpl4u?!qominIM7YF;b$>O#DBLL z0Dniy8hVO#p9Ky>Bwyk#wx~$(b;p1|)9AqaJ~zy^X@LAeP*;ol6*bzXx2Sc`3@v!kL%@MUzruANZh)hQBj zvpm6W$}3ox3N_xrK0s$08}sT9Ke=2~DV_2w$0@#Whp0O3wc1o6_7CU#GwQ@Dw( zL8{;_(Y|HZk|qN&@B>!BC2f9o(gwV-ocK=WZLtwMjG?))CTz#nC`tllQ*YY`=&hWi z5GBOtdGl0F@O;}cRy~*RMAnR^J^MZNQ?=xx-+A3e& z_6W~ckcA zTs=2HLg>6D)42*0O_Rlmq-M<>B+3lk9v3v2I6~eJ&tz!M5Si@@@_iLP4}luEj^6r& zX_3L{xeR6hM%xbLqvivazvCVhD z0zbD(=m;Lskc}B8vIcsymibS_IMbjt>v78CFt)9S2{_qG)S3wzOsK)g%Tnd6WUA>G zjM5zU$@1ggJi&OWcsr4bN<@s237ChC0X~uy`l(-u0~DVop=fvk>b_lV5Jb-9psv{G zqR+TqCJNO!WClp{$hAQ!D)-9)USUf)e;N5f{X_EkA$XL;+{ldD>1ty-4&Y|XZq;fh zN{Vo#q`vxUkMKIcth?Z0yX2->ZiZ4jJgrmgz+RV4>o3?@lW^WK>Cf*!J|*QbMtcW6I~KfSgL($XnXhGni{*lkl5CPJIilt%f>$vx0;3=~Zu6b5fdZF!*<17exW zSAxc~mimWtRmZFM-3F#|Q)V-ajcqR8Ye_c;#0g_#l7f6pY(@z5Lg+d?*Rg*dRs2_e zsEYSS@GglCwNLbPK`-CalX=Rph^Ndw z2~VBWJ)V^&!_?x;0ll&Tn<65jW;i+^giScY%`lnU8Z+9Mos^8}=+rOg-5X+85m6QH z$(45@^BLN3_Yu{<_$??3%ih$ySr=3h-y%%Era$lxMbC3_$Tna9AUhe7qu>T4TtG=OaB=YbdF_FaV8q>ZT*x_<)Q-J7DQPYo&e+%R_)3&i5Ksp+^sa;!N6y@&T5!}#H%{u) z3!({c3>w_7=ho*nu?~{Xv=l&reJn8Tn_QA;bhVx7`S{YIBH&ThRMg*7qnldt=nh`l z)3ynS&tHF>jRFUpOGD9j96ZSy&z3GvP=CDn-XI2w&I&8SwJrV~;a9?yydqr5`jx{O ztLO4>!c}&KP>jht^U98#Xz|@b_I6&%@LBYtH;-GYocMZRut@V zIR7V5|KFP23IXR(WLr{rZ}r*uo5ucseJYTyz3W@{He@Lev<7%^7!UNHsKC^J0Zsg+ A@c;k- literal 0 HcmV?d00001 diff --git a/doc/op-mr-merged-event-2.png b/doc/op-mr-merged-event-2.png new file mode 100644 index 0000000000000000000000000000000000000000..3cd3c523c8d018b7666cf0c058bbc269d9b20f4a GIT binary patch literal 80789 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

Bq} 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≀>`a_yjR{Gh?14ti}&FibGQd;taJ~R{;H2 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 literal 0 HcmV?d00001 diff --git a/doc/op-mr-merged-event-3.png b/doc/op-mr-merged-event-3.png new file mode 100644 index 0000000000000000000000000000000000000000..f0b9c055aacc7dc7833383fe7d20354d606d6119 GIT binary patch literal 69892 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 literal 0 HcmV?d00001 diff --git a/doc/op-mr-merged-event-4.png b/doc/op-mr-merged-event-4.png new file mode 100644 index 0000000000000000000000000000000000000000..aa4ff622a61d3315aeb55ed66fcc88bc3a2030b4 GIT binary patch literal 48612 zcmeEt1zQ};wkRQZ2pS-`L-61Z!QBRT9b|ABToMSu-C>a6?(PH&9$C@V_6MIk_efq{7|BP|Yqfq4yiDR&|vyxc8uZ8~9K zP<~s9i7Cs7iIFKg*_&J00AXOHLlTk@)#9{q`20iws!w4XnTqDy$!R^gQ zC+B6K>-J->?T6Pe)L$uaw>v>F8=)vvtX;krxf}2J>@Ye%qXfgC%k$_JCF9`Wpkbt? zK7)>rVWe4?YgISyu9u%ntWItK%rMIIKXwJ^xbare%mlVWso`O=(8zt`0^v>IugOa3 zL{P+UxZy1n&!pfBls={kU}EE~M|~uQ5lHEdF@yb7FY$08YDW_rRJzk8(t{m}!7TDS zh+*XfLnx%u*3K@&KM129pg8Xq`d>TL)6#F-8aWD6wkpWwP}OD1J(I zvK@t-(2ZgsR3&(z{B~3wPD1EYtfFF(Y|{MJlgZgjG|h51DFhx(0A_apWtg{TdZ;8A_9p2}^7QBmbGY^0i{; zTlZHqNO0fDuzJ3jeIzYXhjID>#|TGl1pfgs!RYmGSWp*pB_a__P?yRt6h;4<6^cd_ z&raJDn4eu_?yn>}y*?s{V4!7xQjA%w#;?BM0{3I+wn^p4*Scun*cq!KXi*ScM+<%~wrW8G`yT%A%*uQ&)$DIU%yxraF zfZvK%{$+nv%;AkUZad2Ix6P9`?(g80souQ>eytW`RemazRzsnGO)bJCp`KN&a#={D z#$Wsfj7=@oChj9@IJ}P52);Fx1$)I7OVaH+kVcTJ{EkWF{JY%NHqu&@dY+P?z-nZbkyq*s~?7T8T ztjysdQbe=}IPa5K<7tG|tuWDch!O7%rbOI%Cox11%d9xWPDECfcd!$Y|!6JDJR%1ZLLN!n_HLlogc6v~JPIG0T7t@0?NMVnyk)N^>}rM^#8v zg^HEc57d9t3nT#P!Sqb@+-e3zSZX6`+QsMUVd^F7QAGxtK1Gts?3(AA>xE3}C93@2 zrwh1>71iC;qSVIJ4NH8LAFJ=85fr=iD3t>WRPw8&)**c`I1-%S?^K(h3D7p^8>omv zX?`G$F1uU};JnhjVJkTCTVj}ozebEk`ZOIA?^_A>f+{QC+RNIUTK?KG_6v(j^An5G zlyQ#rzHsz8_PUfg??aSB^TYYMOz;lh7rt3OLa+{)WH%dJ2F?NF?HxhevhJPox8y5Q z7E*B7qefdR5cjqBdv}S!d(f$gc10<@B0ap2)GG_*~n8r%CpIHrMaYr zCljPfCMPCyr7m$(CbJI1nd^=;4+|yhCex-?u-S8xR{yFu(tW4Pp(9Y6t97Tdr8}*= zRQ*GTwY0j-RqIl#d%AoYcAsy5pQ|z=E#jAXkU|Ah-*mTznXdi3g;c%iEY2L~+~xe_ zLHD8Dq3)sK2wQproAr8Wx%M)V9s% zY(32N_5~hSPi~eoOmY4ICs-HkYjV4Bhv>%5SGE*+rQW+<3w@)Z46RCZ9C*FHx?MHA zp4(5W#61Q(mXqP`Iz zJo$CN^vG1mRJ*ELD@d#2f#`wCxBpS-#_Nvd^cd+AJRG7LoC_idt`Bhy!|_ONi>C-5tat||_B9h14PS{8`3qM!vwdV0H{&)5C2#X+Ag(=)E~Z?{Vi z^iMUY$Kl1{-{p^o6;irk4Mpl!@_rd=F?grr#MUwFi@BOzJsq4vdLUbR-gY`i$T8nO z=(-4vkyU)C(6pgs#v3{pJa5>DlKw$;pBwFxQCs#28&hV+EV267>@iR%5FAK~N5d$f zk!wIj-(3IGb39dIEHoh0PcPhHSbss=M6EhI*ALlx;3)6c#6@2EkbGhS6QLeS>q95X zTIc~zMt(Rk>H&{9QU>C80Rskwf{EcGO{-Zp=p%jO=yP}ruwtvS=GZ-1Y9Na}jxy6}5oVYXJz zPjj<;Oc=?pv4!8=O|H$u=aS?g4mt?6gB`!>RQCx^yv}oR+8Xow~`c3SZEz*^#?kiy{C8d)Ht9qN)iY=U; z*=)^z8HG*0XBcbFJG<-LG5k zXnyi+oiA(IY`(v&CyC0qTAFPuu)kU2@nT=u>eW5a)wCaLTJ)lQ8eMFM3f<~2Eq?dn zy*5~8X%)UQx)mGl4wV8$6_f4@mOlqg38!w_ovA*)dYsAV5%T!hN$h@_cG18R092w= zD#^qbvVQMGz{g9(v2gBvbl!!xhG)t$fcdU>yP z@p*Jpf7=qevwq!l+%Kf&4ZU?=!CqIL+3pz|>%;Z!@+rCnUDiG7{%#0bZ(3hBiTuh= z4zK+4GdmlMdkyJ$-vEp`_l==#7o9Kv`fghxdkz9)Td#-qC;fg!$) zOEoao5+CBmXY*T5aV;sdfO{CBxqQm>e7Hw_htv4EW9%7j)epDbTTiCv#ncT2YRQ-@ zD8SIal#yWIUJ<~+zm#6RB%xP?|5KKH^#SJfpY^aXFhN!@aDTxlzT|&g(J$!_oPXu7 zW52;5zC2;QB=;=Xe?z~9WWD}(8K(244Ms#&Oh)DPut*`FXT)`ApT3d&?+_D(=DPG%0~j}$^EWMpIlPG;tO0CCB` z&|jVeDJ)%F9QasR+}zxl-PoD!oh(>bd3kwRKC-c}u`#_sFgbhJxfr`M**R1GtCN5C zBMx*nb+U4Bv9h-#`=eiD6ML|WAO*!Ag#PFH*Lwopt^PyF&iSuly$q1$j}{hI=8r7@ z)AtLiz#p}I%2w_`8!d4w+ZUd_&=BHf;}G~0{(rUnhvL65)t!M(V)nK#h%Q3^VftUl ze{cM6#6L-D|A!C0y% zvJzKPd&yrW*`Jru%l*Sk`XhfyiXVGZ#iw9kKEcR{i>SH3I!K5AOe8VgzdU?Y+AD?? z8~qi|O^~XQh&hNqsDM+d60Yek%Xl(&+Hf!GU5m zas}DEAk)-T$I)z=pJ0)+WSAB!tYjzB`&X1kaN(aZKmPUbs|${ThKE17P~GD1)qlZ6 zenwXG!u%Wfzj3pu;N%?=;Y!p&{|6Qg&mr-@`~Nq>3*|4d7$3W|u}U?H{)hU1T9a35 z-TwF68NtblcEVZ6RO;mYAMbADKe_Z@!}+tr56G{uA=F)<1cLvg^<_Ya|9?O{+c|Dz zDY9}N+O;dV&m119-p`Zow5#4(0beEK0vPqa2x->nEXoyIeFzE7ahUzb}uMrLl?(8r|*$I$M;U6PXR(R zZYl2RRuMyua2>PCfSSPcjfL{f^$}OUe(S@No6}&O63XSpjt;e3MpF`F`%h74qM5mG zeuC%idcMykv)u-bS4g`vE!J1gRSZFTC(*6fn_6yn6Jb|zK`aXuzl)tR8kTvl4w`yFWzb)>_iZeFg8?iLV?I3h(M-8sNyYx7Y1%U%qFQgNyxCo=!&6Su z)HHg`es?ZaPdkMTChjvb#7K)wZ3q-z__I^S%7ju)eMpS0RSU6;4z??qkw~)1p#UNe zP?=!SVatdUQsqn47vx@S6v`(_HwX3PfbdJhq>J-41_AouG=t_x$>uALie6`7Vos6K zCfi)>R3?%_eg-vx!`f3>r-dUzX$5tOs}rBGW+frw%NC&1jBo|TGmlZtfv*Ne4(ZNN zx^#Xz&O2iQ=JS#jz2{%nKuKG%8&@aa$b_T;u6-ujeui!X`Ykz?{E_0MvCAg&i%Ubl zsa{z}dGlc?xC@c(P*!+MJWwE@6Rn^Q60oA?^Qlt8C_VF3Ea+YJB=Pb zJNkG>`kdZT=Gl)eUuv|UOSL|P=so`0XY1T$o1wSZ8ZftR^^gHl_OJRl=CEpN|TL@136UtsuLw%3!f5rhD0$J(_Wk9SQWImdvPu z>B?m;>z^TBw(*a{C@eEg4Z_Fui%(I48%jfHcJ-O)esEp8_MYIjXWi%ewD`I%+u6+d zxwDJ?%*5@byFucs0iS|e*TzHcza;3NBIhOm;|8o#X5$HcTaJErb=WThER`w9LS}B- zrq;GkFm`&RYpIo9zw_&$YY@0{tuaNxgp75ZN1TUl^knFlY_brRlZCz2`x!FEEqqJ9 zAJdrZDJ$f#)GiS?onYWlTxi1vD$WvrCjXRRESiLK8WLNiC9P14necg&o|TYFUz08- zH9S-m&hiw#Ekq2ct}~37p_P>E)tX$dEZp8loo{!2wEJ+0kM_xH}E#kzWIi%r2iUghqPYzRUc3Lossg} zG%bcS4E~mx)bn}KHK--(X8+L|Uv4vCniL|Rwvmv7QXbdyEjH=EHG(z;;EkW@Gb0|E z7p7yaz+8d*WtPP!=Dky`lGLDy)D_6XJmc)s#ys52A`b383wP0Kp00v(gv-#n{m^Ap z>+W86&ROT-jMKjDtqNaPNM(=SE;Jt`#OY{WYZ&!xFItu!k?Bjd3_16!{=NK6v#7cF z$t8|}XPusxLCrn?qGo;~(aXx$h5+;XjGEh$i$uDaZyI?{B(J3Babv{AB9IiPHht(v z2q#vFS*1 z_fxmH=Al0$Pu1Pib^ZE}n}uz}%;!u!m+P=uAHruV8DNvxm9tbSbT`R2J)mL~4edpm zA{A={{Y`1&qQY_u-X$*ZtaVfe^x0=P|4f`so4dW>*AHIgq&=h}EU$NWZ87YEzq(-{ zjf-l38nB)7h)Xd0yhYn|w937(Jq@@0`uSFx`DsQD6dd-GU6Yvp^}Wlrx;JvM%qk~Hb(NRsSfCwcWo9lF&SF?iWl>B5@9Xug_I zb1q$}V@R9%(L-wIL*FW2s!Fl{d;R;Yl3Z3amzc(#m9GE`p$MV@0M3VKmQ$Xfi_`?dCCf| zfK>kk-6}XV12c(e2MhMr)>gm$_7vS8c_HZ>ZC(eL_?MjT!!5VR{v_$<(0JU273cVU z^%Y8(Yz0XN?J%$F%q7rhnFPMq!B^0{THtaEj}3Ida+Yomr4~Uu>R@1ERgRnkw(+); z>9HrRh$jo4-fi0vB!+a1#BWa3c3ho>p_~@jMwBqBIdIX$R$sl+1e%&RZ9VQ~W|Xu( z?pk`RKH7tGck$BgwlsJ1wL21t7Bmy_=ajDIyz6-N5|^KyAl_$-?jBN$ncCOD5^&OG z2_R4C&H_=gx|-jn_a8ds3)N@B1Hmz0GF9{GrhCd+^a17+8i!BIGz{ytDVOCHqw&FB z!=uV!EjGXEuD1G@V)-*Yl}7n(7-ntC^7Qp>F(IeovNZB3BCEscrK|ez;Pe(g zb~|6i47Kl@Zzxg2Icf_|)-oK2oM-7tT8)IYH3C)2Kd&8#e8KVamXirMxo#I3dA=69 z{zB-349U=^zPUMDe`b>srFdR*S^FX=pZe1Y-L5uv+I=XVvu!N=q@I3y?gIh^jl%Z$?skZf>rd0YW{ykmq^#rK&OC$Jje$Am&%ZtoIXs5s9=aI)XBF=!&_NwhM>*9r6XTOX{D*F3RW{zRGePJeW=5<|z+6+_9cMBcbSQpjO@SIA~ ze!73)&u|M#D10O0+mG+Xm1cnOs`WbMn!`(IDuK1%{2R)6Ra|Pr`9pUDu8BRJrr=E~ zf`cD5`#wQ#`77?zd@Kkzj(L=!YxFYkzt{@tv`pW2iuZ&E_(H)Z%&Zd-$6s5s{QdjK zm(M=O`IGa;0XXn-+k=jY|_b4(*uVw(}pgV^Y?rK*OtC>8G>fJX_+${LadLFU+fI} ztrqQ)c(wW!>C^84dLI1-4$<F*7D|9O%%0zmS4xOHTEgzP47=VeRoc-n^B#pf6KN>ha+2 zYQDUo71BD^=Pzf^n)lT|j+aNhk^F4XrCs3?Q*l=yxkl(-v@hK`m}<(6w+!EhULBQ+ zeh@f5SrJcCQc*%tt5P$+2mNs9TBK0)n=uYr8*;Wf-XiDmh>Ay<8fvD-XqG#}93;Ce zh(1}QJrP?PY}r$2pgg&gPpNf#v2bNRz;t5uv5O%k(_7m_nKWWqpwkaxIw0NuJ49j9 z`m38=`_;rmg{EeMhaHk4^ETsGUi$uLFCvm% zQP+>wgki(oA(fVL+MuJ43w}0^`0pTpxCxpsPR&7uWA=VUwU&mo6IuWaA*rtqR~^T0 z_VEzr@@<#q`bO3{>9E1p=fmmP_3Phc_p@*oxPfK=h!wygK?;^!pM7_+d2d*(hge(6 zG8b#cS7!#)BG$c69y*%o&}#T~w5=*usPvA^M^g9Se7C>1ugQON-7T#uPgl0$!X#Uw zTK3H>pDh-^dVS86G!!0lEhk!MnNQ@Vt3Z=WQMpZ%3f(F^=V&Pk6^gipQdJ+))7x`? ztC7!d=MtsSbIThZ9FE^pYeJF1Nk%Ez|CI+EA39EQvencvm8+^}V+u>vrryNDdpt6j zEB{vO#3s_M#WTPmVSJRvZ7;}mtW9fh`L#7S#M&vLWo%APLmPzIU~Pvys&{xYkEVus z^*xkzkYMO-;{206dIy8WMApIREJLNX^D{#YvQPc85j@^IVjuw_6@m5IkmHnI<-Hjt?&nwPSm9+VYtVG? z<@BsJ+Jta`^Y{DxN@PUnteLrcHi5h4GVJp@vNSw?N{_LYsR7t;OeiP}Ekz+eS=OhT z@e}SWMdOf)tTcLofjV1>ZH~F+YK5PsO6xt{y-@t)nuPoH9cqU?pI@hFYp|%(+c9vs zR*)BC=NDQZnrI5s0|UP8Y{r4!PI>T7VG$09PyLuXf=Jkc?(FD6?JADza^=u|-{pZB z0Xmk}`-tb?HoO(^w7Y2?<4I=?yLD%2qt6~4qlW=QbnvWh$GrDu^Mthy-)NggNOBp{ zNWcLPd}Y(NI<_eyP6d5hxB!2Dc0-d4y17v=j#bRrHg9sd!?x|C^({(I#dQ{?j#ale z3^Vy}n+8>qA~4uqs|Jg0;@_G0PsUZ|{mnuS|4jS2%c@aPRl5H7Hb+ocCSn|ffmZLb zk&)ZR*y9ru#Fp+52hwF-WaHGlUb~;^6e2jFD%u`fpBRg*_=AjZ76hTXFP0o*@03HzI6_!q+;G6 zkDwk5weS(B=bbjbd$F8D|86;-^wXS#5di81uJRe-jyK=CXi_&(dl;NdFr*z`HRaNr zGv<;m#Q0Bg$IcQQ`5cK^M0>0@Q#vVn>_m;!F{Z4jwQY;*%ErMY08I!;@RLZ7o3zaZ zeOjO;G(`Y8lN=Pe%Y1dr4-?ec=k|6@L5!{^1F>|fNpn{yia0{5OI|wphTdP~Rjg?S zf&nqLI%=G((Sx(qmHigutH!qlqRVi(^h^|L|#@;>HcW|&O&vCW-`05zf=dET7@Muv$BDT_Nj2XsZfV-lKz2SZKW5i zA6O9DX|^m>J;+H(1R7LIO78C0`e0e)b*^tkr4s!Vwhw_!4J4aqs|S|Yeo`xU zctf8i)sH;_8e zOiJpoIztBmZ$yzn6j!f$6%dLEaS(C1tA4Jx9z^!C+YzpBv;OM9 z%qemuxW9A&W}duJ#A4s1_u7GS8IEMkedE#T>0qJxFO}Vi>w|_Ts04BDn&cO?RCnoRUJf?Nmqp&G7Bj@MogB27eFrA0 z-a9^}F>fHOgXwOsqA&BllT#-+;>6LD5e{NcaH@RwSDmuV7o{Isn=tLb&U@A%sL_nk z=r`chw_dOiFDsk-EXq7--2ZUBPP+MX=qbG{N(C(+Yu4n3ZPs@64-@a!AjA9ILFne; zc_X{U&_$s*IALIjn`je@1b+UvLDy}TFEVrUeV$DM9!XV%O^#~CI{*p}q>%18>%@U76n#e|M-lu}^$9 zC!d>({s>muv9E%<(c9;mplj`W7Fj3Twe>GPo$t-1lGBO@dgETQj6Xj0Spoy8yTXy@ zo!~a$e#^GM-ox_`j>?$chRT-R5zkn<{+78mU{sY7pkp7}&(#UwCxp`R<|7A~U?t)2 zsp@7FCV~_wpr%2#z+X!ik4F>5?L)zuC`S#m*;R?z<;~@VpP)pI1VKf}!5>m`AI>@o zfD^YfauT3s8q`p^gud`;sWfpj>S=Ys4nT3jj+FhtSOYJa5EVj7}q4lGR4cotp#@UYkv6X z8%pGQ+O1u}A_7&~6M(eq&%Qn{{R+Qb!cDw*@RM$Gf5S_`vd72!;qc}_=<60G-tlp} zvdv4pS`!}O-NVk9VD7-(F4qqF0!5>eJHY6R5pk=7(sc?cH^_504R$w=wAn$Jd)cy0 zNa|=Y^zS;VlQgQ+ynkWmuDMG{;Bn&7CF9-!YfyCe9X(Hx{r2RM3$jql9wR=w{)r|& z^!WOzq=O`Jx^1krpPGxFCIs^`UfBV3NRPn|QW)O3pjytXZGlEUimZQ_3N6g0(VwPz z&j3XTjkXEbkZo|$!+MQiRX-P`Cg3`SqV{09Kuw*46^&X=_1-B{9U|L2Q5L2nx7hoW zBh0J3&%)QMdpGuG%$iFVn@;XZ~!VzrY8d2r7)$nm}b``v-uIFMxc{SuWjau1G zwh>JHsAx#vv%1;O*(x~TeuK|8Fe?TP7e9LD-b4E-lb8h3;x5WN!T zpFVUdjE5o@?-@cm3LAy}va24K#(8@T>0sV*{H4gFWjyHy|W;_o9LSS zHGP?#xzr(a$4TT^$UDuseG(5bf;E|<(ho^qqf5HU`q^c9X8zbk-HDF{VrJQ}`y zR(N%wy;{m5`UEdWZem$m?Is<5&T>HSidx-QSIrXB>{3AD%{(SHbVkNVO6jI7ivgYZmoD0}Bk`zoCe_t7!u3bzR255%6joF$vMGBQXt$JCvTOvR2)JIma+Vbt>KXl4vr+ zA<7I3O;I1)4#9TbZC|*_^;=nG#2xkGDwH42@Ycb2Fp-wyo+(F5;~)EXPw(Np^)|3l zeO!Bb5zXxHj=6;>$69ami0UW#oHj=H%^S9LXRms6HoHNswj3r$puTxs2u^I`9 zlXr{L5?{1cMYbwsOd2$cOO`Keu~sW!mf3acdspYBXg4Sy`l)9kM7_F*1G&Fe(OQ&< z=GG4*&oZVKWu~@yX4)l0HY)Iv2PaX_(Bcxhp876#sj?d76gD_~@7@KML_P%oktu zF1d{4Z6xc@xCw0|7TVv?EOiaC1 z%>m92mI@A6dm4u~T@H&0->m%efs6d{IrI%7EJx{kEl=5_U+%icR2R;TrH+p{N9R0x zM)xfd=D*x3&RE=(K=M=zyvwog@7Tl_?a-I4^sgV>{r>51KCmJ!Jp9)6=RC`8o;?&{T!BY+#Q}UeBjuOv9xnGi0pITIJ2G3B zFSjd4F_*tZTE2QO_2L{+V`~~0n5a@x6J{(c0kHdI4?n0wA~*>ew6_@(kU6C&_M;&{ zP*Hfv6sDNj2$b#CN>nWFazgv#B}~#!sU#%PC*H0ixr8_>+Cj@G!JK*3)XRFq4p7l< z`Q`3WvPR^Povgm4z6fMbmLqRGHC4QD=oftmdTC;Nnh@D;YM@IfZd-XhLpHt(zp8Qk zYQexHRS_)lGYIR_MXk%$<)Ei%Lf5dkT|#Sa4~pZykZYw#ML~qGKv%zFa55jxr(NrJ zjMmm~G9R}u5Q`Ksb$_^&2e_fA&y!ErW{LAnqYH?~)-YoDhgT_?RfCa5DkNhus+DVh zZLV5ZTi`>D4Uw0Nov@Oo=un6*svs&nDhF9TsOS3Hq=$ZD`vJ<=!SRns{aDCW#3ijj z6|ceie(I|b-H0;3hrC{FL@_QWD~A6#vZ6+%+FnOEHl${x$1@`%pG$d>By1>4qD2nC zzT_0K2X_Xb-nD5tmY0Igfp811S~XqE z|E?GRjN^Z*BchX$Q+9Z*VM`lfxX5&AXl+he_d&y09kM`nii~U(8wz@*p80z)w^&4Q zx=5goWJ;rm;UG6$K5Z^bv=*?LT`X*|xd(EZs|Yt*k8N!A2R;%HfZu@jVzuRRSgym>COAV1o5)q0EI#(fNk2|^V|)vEuNF+ zg`2Qy^HDBPBH=>mkdhDKMd$}(>mceWt?y}Apa->AMYsyIT&(G_d0hH=7UWv9n&qNZ zin!XPVJJ#Cxt{Xc%>z+f#Om8RWllSN?UW|7`w-o9S?d322Mg-Q*=(VJ;1XnKI6F42vPI8 zU@Y@-c8W{bSnb?aff4OL0(pNv;C_OAkpn3%0CT~POsf3H?5fze0F{btHXU;COYq;e z7+$sD6P;XNu4C(H1Ftsrbzxi8_(%#J^SDy6JiJk{PG3}tNV6_D^OB~YrfW9?elo|h zYpqBX0&<-*zzQh}DA!?nIWfPVkFrqftLP#prwMLQ*b$&pQ2?mrg*-mWy+Y;S2ghEB}=~l`)$KNVZy!0?*Z6%&%5B1%kera1^HO<>M(O(b>qUL z;!OgU5QP{jkt*>Nj)yFb;S?$~zzaXALuQ9}TB%}Ou-1U@uV zJ&M>QYElDNqL-S>tI|QQ6iTufl?vh2qC(tcWRfJT)ua=54eCmZ6(cwk<-`Id<3YNX zV&6QNG#B(e8GS1?%4foQAAmMi|Jlg?YchOTTP|8DinVEm40y%IKo1s%g0RTzoQpZx%BGcis3tS0rik6v8nv6t*Hkuh z*>#IQZo)(=6m7<>d+l@3E_!M1Zs#8=el0dqC_E`OXqC)fR9D;EJQ=oCyIxG5#!)ch zRPSn0Cf#kiP{Go-@d4+mhn0lR2UEXY8~;B}fW1?ASzoHaG_}Uk8v2jwzegUnbYy(5 z!^H_XRZE-4G|bf0DBcgyl&mqc2~yVOSV)HtSJ9(PjTTYFy&q0~5HQcrokE%Az$?&z zgzOq*^9n2xhp093@;Q#%2KVI^`_-vkO_Y>nOo~HyM5@`$IMag^P|AVu(G|FsUvt(}pDZd!22`Y+GK2s5= zQHPvnO0V}P$vK&~#f5Fkr;B>$c@D9?4V4d->%QSr_?UYdk0hRc2iL`Wn3M}u)Lab> zjLRPI&(^U3Fy$f1r)#L*p-&IjfSRD;!t`dG$E6yn2T8Gm5}ej$&fV%E#Yw-WrBnL8 zHW#UCEQ)zDR|Pq5D(n(vq-sj zqF!0-qgu1zs^6SMMIQ&UQXeQd6gJN6uFg~cTF|%`>~X- zcKFqD{^X3+0!r@6!%!q`Q)sqSEHvvFi$BX3N)O8??XG9ULbuUn zN`=a&%1sB$6zv&!Uk!t!yU+6jCLp&27qkOgGb-b+NvR22v{yqr1iVX|GS z4Z)@zY;;^-K+?WGS|mOE8e(5+&M z>HP~kD8~e^AqP#{8Fo-6_fM?0AWO^WiuSv~h_l{xM{UdRfo4^`xM?{@E8rjdOm$Y% zrP^nkNruf{iq`e4d)r9{V;O?oQ)a^4TwH@u!cQNBpC4*|2`z1ti+$rUm<@0$b$DL( zdu}@k$^#(gy_`>C7-H#YRa3*@Uvf&}vQ2$B_Iq~DNhv9dHy3&)*7lf4<+HsmMen;E=}EHjBY60ig$Y<8(Y;&D+5xV=jFc3)ER2of!yG@-O9_{+ZgG7EPz?=(Q2)Y!p7{EC_&VzB-Gh~Z@>gb}p* zH|J1-j+6Ned@j|S)m_Qe6fUtfdOlP6BvIU1K83~eZkLb}ogQ$a2d{;q>_EljfXRGeqB3U!fGNd# z>auR^m)#!oJefM^Qr?QlVzZN8=DKLYc}UHs*t)!VPLjS^rePC3{`VSx<5%(2NNI*& zSJC*INh6u}q^d9By_JCSfTx*`r>Rq>Eku@`AX48CVnJw3#Wf3w*6mkw)}leUT}|VX zgms%x57$zA=f+pTuk>)6UB*RHm2WR5l{WK|44g!%8p8}Vtjp>&x~@l&^yO<XPB_$yEy!ZR^9Fxt6`1XUfg_FJ2$-*Nh1(W8;bf1N)5;vu%ucD)pLJ|^skwmMa&%dPD)F0uI8Pe|<5>kd?Zwv!$%w)XvKugSZ8sR|JTgZM0eZ)3{C@{TXR_EgyC0 zXm^StRAvzmPZl)nmwsBUx6Tk({5ozjHWpA~E*bYGFDC)O%1#~Ew^M&si=thp2kO_z zD{@hBn>+Funu^d^7q6R^_e(c*mX@Cp`~=dnuU9 zRWpw|wauTt=WnG~caFk4;e<&`At^r09k-qwesCHTf2Wg?I)F+Dc4UI z7N&?Zl-9DUDnFGAkL1GTGIVD~7rcm$Pf8l{+%Kb*V{XdDCPhLyB8_P{te+o5VJKZu z8ffat_I&w=b|6vLj!uL@kJa|apy!OfThl%nW0JnBwncg`U!ePX5(9-*I(T|u7Po`d zFb%`#W&Jm*sjD}6ek;n0GF%GQE1hesJ!m~2jSQ@KBS&KEk9fRTTvEO_8boSBTyjE_ zWT?lOlVR7m`fZ}a&yV)uJ%_Pb92b%M$`^lXK0V9rNhRU*h}930 z4it&*%ulz>Z5I=Yt=(G*ng%hulLakqn@WCnsyAoO-jY0Li<7FZV@?0C9lxt2;d-A-DD z4_rthHvN|+aQ+SEF?l_bUlGHoeTC_(+`-9e_u{fmM_V2B5o zHgU?cZ!XlwH-ZZA)_nzp^*x!2mMYhAP<}*%aB@S$G36#;t3kbC?rSK3Q@dEbR^uD$ zVewHFy*wBtv5z@n!dA;orU#m?$&{lq;;UOXtZ{ezF(f`NP$Q6v53nxURLU|^n?G;q zKUrh7AB%Tc)lb6Sg%2xb@%-|29hp-fexNi5QoO4iCg zk!|8znvGoG=!4s`53LlbkIS?|?QgEOo3*gBE<{ouk(#6CeXbTp*t`5RJ15b@oAoIB zdX@)r%`~2^K0KApT={mzcCa?}i@gscY!Ke$x<3;&2auIuM z#LV7H4LdTKU}4;?{)YQ1bkv08^8_Dek0=^r*p5_U0!NE$0QZ2#f3UKLZ15V>r=Qr3 zC(P`m2|fzBoHBa#Ga;*uWg3f?l1GyHaPqkPOlB0Tif_ z8w>^xCaW9#U4k#Wcl5pvutR(SSTsDk*}(gMh4!*AU}-blvk5n@`YE_rns>6f0#R?B zwPW>=X7pOmyvfaigperg4e;)4QJqZ3ft?NSvTI!YC#~w?(8}n!QgU-;_P^gkE$LEA zq%?o|5`7VvqRfAys9E9{Be3q6(o!ruuuX5^ z5Uc#^G`!B^#UvQoR}9Zf^D>V%ZQ4w9K5eJragM38{a0*C`NJ!OXeHsti-XleoV50A zz3jM-8F!VDA^+b!lp#+9{&A(X@zdh8B+d^CEVc3S4p=Qkp5r5-qNXmxI(~bS5&JH< zlHu{mf?(%nRKwN$VX2rtKC7Dt+!U@m&W5%jXdzX5nP!Q39dx=ExThqamd%=!dQs3~ zo_{#rqC)@{iYpIui*Z+qSS>A#vD@R*t?APS!5tQ>x=!y9LHKA3w<^?Ib`Mhc3DuTH zk1l>87@C(Tag8XD%W{3-o9MBA`~G8RLm!hPW{*OQ%JX5vvQSIZ=*KO4`$(zbaAN*x zurEH7w$J6%d1NXEc5r9UWn}G$OQGX=u4*Khil|#=URtaBaz_t4N}}LbM?6QUqHs2H z(yO^IY1Cj9_mC>sTmQ>|^~!~D?ViIZMThob{g0i!nAc+B?+twyM0bN5IKRcc=k~K@ z^wuI+4SZwHv&nw_%AyAeQu*NwUYs$e6JbU{T#_8#_)F&s-kZZQbqqiy4cf1nFd&MY zdie&YR^N_;s-av*Ww4n0?Pe12`SIE<-QDldWd5rc4QpakX*p6RLq+RE9X@9GCY|QM z3{7Mp_I20}8XgM|CexDt;Q7Ot-_yD{Zbz##jy-bs)Vmo)6kBRm%wykjSI@2x%QXX^ zuDuXM$l&run{{ey>Y`g9&$_LnNi7+tlJ?ufTtacz79C%XRW>UWjR4R zV?MiobH&nn;;+qfE`UC7^DkWyh}rle8v}itSTqfNz%@_h%Lc<(;~u|_gPNW=wkMw( z?~hv20s%Ox_Ep;(SZNpF-T4!@I_hzm=htIJqH+Upv~QJf3xuomB~!oBRIA3l&Qk*x zM*td3fZ|BSUS*2<{`)rK*W5eO?k)=qZ2RlFd8JpHGSghLYy3E=AbjE+$8`O5AOJ7q z=jSec>LqIOsn-m7)tTl^W##dyd2Wft)M(IyT7_)3;;M8N1*#3I-05R)uc;rirDixZ zhgN*zx&kwy4etZjCoEysz9kXwchVI=Vr}Ex$QSHd4k2~$r@;G>Sw}k1FtGb}zr@ViWcmOQko3dE!5hi&0SDZV=qO({QAxh;MQ#VY0f zN<7Lm8#W6YlS+f*!LXH!JVD#DX@?93yNa&u-cA_e=jjDn=emK0vOMb73|i&^c0G;1 z&z|ZoNZg0$0!RZ9_Pk#8d=~o+=K0-c0#AF4nP_3+8iF%Gjdd%=Vf^E(IdcQ*`&*F@ znR-FVOD~rA@3&gUIK!Id&3p7oZArrJ-hMg^fow?A>v1=o1lEEb)(+cSUh>n6defex z*E6CHXFfe^O2k1Vo;ySV=%_lUL9xqVXg|M|Kl{x)o5g?avxkybz9_KfcJA#pLFe7P zBwSU6CFl2_+x{P0ZynZFlF$FDefV- zy9WExnYs6yJM;dPhv#r|);VYIwbx!h2{Yw1Xl9h}cWQks#(a#DE$25s_>QpAckm6N z-Ws>G=pU5AL>MWQK2aSM0+>x4SC|Qku`JWcI^N`?HIJCYD{pt1587ZkG!t6wdw&V-jo$>-x#JIv=jKZ^48v7#dbb6Uk3a zdM|5&g}@46SYUr73_GBy+NE8G4a5@iFoq}~O5JxI|IB<#pw>e^muF;7tr3r5p(NQ& zUMj{PX+x8!fDy>E;N*`#^1Jv?<5i`%0Z6U?1Aa~eW=F)esdjG}4l^$LdAR(T3?9wz z01bh<&>yO+k|U&*>5ZrX8L;2{rc}CiwKM56j7eHh?@0KL=YB_?V0x98ils7@da?2a zbt=|Y7=Q&RT;g3GcefaS;2XH)e)F~c(e)-15y@g;Xh=xm@JrjKU}LFQXH)lqH-q~Z z(AM3yT9HXZ;sp@>>I#f25=@A;OaN z!T*3O!Y?C;JJX4pPuiN%tj_L*OcW^Y^5TF)8o~7%{5R6B7U3E?@U)Wj9{pk6M5(It z_b#n)IRBit&$!;`aZNz=A8m3AMe|}clD0-elJ+)L90+ZmtG6V5B$!IXGeHUbv_e-G zprewsq~*z$i=sGGa`kSQCytfN-g;ukTdn06s%wtVlx{T$hLI$eNtYqCzWY1tR_mJ$u;oG?iiQu0$h4Z)M|#I7GV^-7DJLmU>i=w#77 zjRRivsNoQ}1Z&lKH+#$K1~H@PboKUgQLkYQ;{o`?Gjih&-!R?ZN}71c#_y~Tal}=K+v87t>qsXq(QztyAIX%$3h_K(C5%#Bj{%)!97>jw7{uQuSAq0b-?B5J zwyup>@JPmk49d0oj2=a)uo6vzoZ8mkIDfbdiR#@-#NeA%nXP0o`GacVv?aWnp*I*X zKbOf1T^+d(rtDq`GL+Q{bFGN%OlKv+hTp~5Ebq8G_a^55 zg}YcHy?lCy)cy6;lR1xKB2ft^Q-(hRunEd~7UNUoFu{!y6rS~NM+p3^+e?2lYxTyW zfQoI8N##5)QvMA}*pRxs^)(pgRuN}-;-yy1XDA7?b@rzv+%K6`{)s%}6O;UQb6;|XOMB8R745uaZ%eQZo0Ec z<=976o8IPSkEAA}Qp?ljI%FV4a~}Y6S-iWqe+v3eJe_WM7NL+(uuOXvL_;fq>5}QW zUj$XM5cAUPVV^pDnT_8g@U<6Nn?+1Fkzqq zwrOek2`kxGV(9D!-jgO(!tE#Cp8oX$coZDO1T~JEZ`onK-oJ@>tku!kKCc;|<1gy| zp@2ASDYQ`39Z3F^EZIV?W1V!w_3G~m=kDlw+qEW^ zX_C)!gq?O7wa2%GDrcjVCRDaI@{6Wy%poUHh0p2xBFlzaVtKSOH`}0caGtu6M*gsL z+=TT!{e@3~a}!?1zO<>>r&L=F)inF>ge09YJ`nfPcdX_`Z<}ji7cEMx$AW#Nl^x0zg%wWOQQgEPPG|SQT!@R2ss*?f5+9D1s zw%wsG^m90$w+hL~p#uF*%({B5@!7&WmJJnJq|+I?UZQcHA!r4h)0LgE#TwgCf2C^C zghqOtHucsF{wPV*tMJ*OV5Z_|Yr~tQ_hV54|4exKs;5BJ5nnjO#YDX_y^_N15dN|( zcD_SpEYHdvL`)d1ocIm+H8XtEEV>%)QV4p%6ncoPJ;4o=FsD0_P72oJH;7MO`#05> z5Qo+L)0VO$bMePj$?+4Sr!8i5tWzPn2mKKSe(V%2V7OQI>56z5lD_AIkVOC&g$ukZ z{*PuxFjkhmG`Ncp9bYuB+n(8Vu!H4w+vOeOCF$;cLKPkj@7}Y?LS6noZd`_%f z(y0{&sO{{iU;U~su)NmJkfh6)2I3F;*PLxN>61)f7ZHrc1FLa9wicOil|AqEFdZk~ zK|@b0WAbBd6FS$cZ8)1^ZFJ)G0^%@807My@oVFZ}>#USo1z74(j9PFVB_pl(?6h8` zLoYHAKpD8e9C$-waFfXV&^KsJ%C;J-AN zl07tpxlpJ6oC3hQbkwg26{MM7E=wp89Vci7bPWbPTxk(Z(E~;r31%1d*a}74_#X67 zt>Wy#glSq+CMa#WPd*_~2^QudwAoKTqS}W{O!c`=&x%IRcJs34ZRL~EvjsGu9=Jlt$@@p7!)h`?yBE0lf5#Dq>Bu$;j{w+#d=%pmL zW7oAxj-H|MgZ=qSzRqa@M4?Cj-|; zyWRKkfvrC%QuKzxS>;F`{wT3!*eSR1zqT3OnPLECW4#}k?!$IA$PnmKoD}Fcl}=ZX zzpGz!$@_QYNJRy68NM%2X^|7gJq{Gmx_d)b!+nN^Kg?8_*=eC+nP$nnj4j^q?vq@3 z^3th@hyQn;c(^8mDA<+K3@f;?^xoj%l+Doqz5vhd!gCzb2TP%Lq^>|oy&qlTuLqrf z_nAK4yy+ah@1lI6fN?5++Kc&%{X>T(E9AAIV1U0WLD0@CJNu&B&I-_Z7M%Ne#c_p3 zr^(5Ln~w4G@t>|D{uq=U5H-&IdpI_fqGUT+NEgpM?{_nI5N6I{631TPG%hlCB_||k ztX7DhPX5x%x9Ig*$B#}NG};9YZ8=Noh~2C|4rAs}>9EPO9-o*oS`JraqS;DR#Oh91 zB!}Lv_mlvw4a*hobI1?d4vF8D^`mwh~U^4*-)A@XZ#0|z4?}?KxWhJI<@TGNK!6V=ipV#Z#i`k8@bn_ zT%G13R(T=K8-}^il`w1?y+h`??FGu^28tlES-V9nmR0;zyWi_6XJ0#rp`F!*ffNyF zSL*?tA&D$1)7r{u?iTKSJ$dK8=WJ&!9==*R4N1T08Yg(qSbq0>L}IJb>mEmu@$!NC zQ2quBFuo77SjCq{87EZAcosF<3nh0*k*L=LobrQjQeGmpSwLUu>L~`iiYBtU{gnJJ zjHNM5f0Y?Er-C&%Tl6K~j&ZQvUgO=ZZF%<^%FRAn7bzN-uzNe!fiU2yGcZOGkox$zTr3ulOEGr1%x<`S&_Dej$1kYug)LKOIVP*!=JhHVBft|oAN!x z^TDgNi=VfTP`W;1*c=@2EIzp5JUvG9p+8SWCl{Qy-=8qAG{GIF_bHDgNLx-v8N=}D z>DBCu#~ByCh&UipL!-vjR8)K3P>Q4+rsH>r>c){>Gzmh^uoFxjA&nn?i!C9}fSWB< zn1Lb0=I$)%A>m-0*L{Gtj^(d_lqe}jWGubnOoE-$$x=%u^;i=MAk0{HnbV)s?w*%} zqD9cf1AmX}3_qI0Yz&jtw1I>PbQi^aA`t#`7H~yvMG?s^V?sa38~aMNJ)CYYvWrK6 zC>o{^mE_QX``d(ApNu9j5YXo2q`MfseINuuB6hN5u$_($gikV*hh4d*T9gZQ&$b73 z6%3FnIOumf7&LO<1Nee!67B`fcneX=w(H7K?VFKQEI;f(2Cm}w0CMoL2bxt1Hcq`{ zMrf$3qa{a7*(0z2w)g|z-HipyC=rJT?r`inB=tp5x_U%;fOVM~6fcDQPY5a_ye$HQ zU!t$4g(qzbbvZl$J7oACgtA4@A9x-qaRVu8LJw*+u==z#uwm&^9Gwd!JWKZfT)>=u zyfE;7zU>!F|42E;~DimGEERV`IC1rfd1-yi#ab&(Z?Bv*4XYn zce&J9x6=!V@c7suI?E+(K^4qPbGepqA`mg_kJ5wub;9_M<^a#)BK^J@x)la zyS&_~HfQS;0Ivk_)FU9Yym2-N-_@(1CSHm!$*3FsHzkMhtDy81uaj0bZ~7L@&Tuc* zsYqyLd9`l1R;F4bPJE^C*z5^KRx z!HAph2g+EZ!cw{UKPSsS6CK=E%1i*R*c*`t2*L?gKsZi z=lMN99Yf#Wb9{o2TPY?}mu$8f;pe6L5kDC1z z8WH>$+WhOqoxkD#(b@P-aPvcC=0K!5pvKWNgg(f=P8G(wI?ekIGVgrH$F{r@;issp)DvXTm4@GF)D@a34xT`qg-^~i#tZ097OmZ1|UpN4- zS~OXbZhHw02W%=@w-*JslTGu!t~B*d9{aV%fPG;K2Hs~fcW^>VzW3evWTmOxd)+!) zU{b5|9mRRak8ZQ>D)Y^pIE+{(-S{Z|=a?k9p&YPZf?%^9GXpov18Mk5G(IfrjbSrmC)>^Z4@GJYYStoM7ca>}P)dc>`&pP4nH_Y?Ty_I2&pyiU|r z7~GyUf9Xv}o*9lO){EXM0w-^jkMv8LsoS3$C67I?eTr(kXC1dc-phPuuCafLY9Gk0 zh=^_1D2>+`y~bQ*QfD{suYx?q`%1M(+i$bPrd$vxHNG7lid<5Uf2;+lU@q#<-W#?Y z$XoHwKiutrw^A%JW8fvI;`TTg@VP|81)|wUm9Ns#`aGb(>2fWZQs53c654aBa=KES z1U+ZuS?8nXmkrN?O~j41hWs}7`;i7haV>_nDDuN*%FPA-I_ z=elGy!rg^n-=@%oL@TUsw_va1Mo#k~ygRV(DFwXR;zNloa9>W^Yd~^GNqmjBfY`-&{ z15O;qc5xjm(XGG)GrBQ1-s&QW81MoNfwHbPCwz9=(g?l=PtD6S7d{@(8%4yrzkUzO z3J%!P%JB^d7c|uNH=h^8j@iC4Za%z}B3BsHs9P{!G0TkYB|PC*x-Qn9!3Up?-b})- zlk$cVx3(Vp0b{d4-Xa%Bd#7tQ=efXGsvG@Dz0~*GBlPxBPdb8E@5&Kci~^ z>qgCzj+hGHeXku2atp;EN0{7u!^}*Nt3(P7vR%F#F>(WV8+I#&-O?43v9P8cQ+RPw z=|N_d0Iw*#MWq%GPWp$LZ)y@(oT;rRrIvu^<=%h;oLbK+oOcT0md<7IP~e<6FGan zj&>!om5ZER#ApRLfXnqP)%-Z?B7ne^k(HO^BaWCM8-pbX?g70-;WxMg&`VIN&8rg5 zWzc(!$kJYDmBnwY+QP0%W8-^YFR_}MpNCr(NJ=A`!RE&3uhmlXE$C(EEIywL$tWwc zEz8u-6XTS6^+Uhmn{qrdi}ST@r!?gLTv}!RC;@lOh0+Jv^7)|z6z$))geg2H40m5U zW3%=b`ln-Ybc>g9Jb4++!D0hZKpZHDRFce`6j*J9Zx%vhT#DD_K20=-4>z6BB$j9Wv4Cn zg3rxLzQuT^|6mR_kS1LL^N;m15wC5s-e0~!gm8WGWdE5^_2;thNokp$a)`fDj)M15CyqA} zeciIn!8f9DNCrgP@*DJ|c9iY=2*|NNL7cz8PG*;|=v6Iam#n(gIKDnQMWNiZSiWs3 zz4gjZcfMV9Jh*Gr_Eb)0mlzX!_MY2>FG*B?DP;|JYSm+0prP9Fp?U})@m%oF)Ddhd zUt_c^4%VO7O9P0CRh@pgo4H?Xa8!6>Yg#5}w0mp6Ddj`RT~^;by)WW~bqf6^Mw}{vA{7;lpX@3j9@h^ISk#WGR4L zMQ`7glrmpzkH#gk>JC+z4=-;bf4NBe+GI8BxrZu^4Ma2ttjs7{tB=}dS_N^D7nit+ zeF35mDW!#6ln|rk^^O8IRro9X&h0!bM&^LsmmxSraSzy9<)Y!1KdHP)zkZNCZaUp9 zjZ?nmSY5V@7AJ^71O8$-QWeSjV6lz`WXcw4Ic;kLnpQ!+&TMJ(>_o*D8a#h!YUAy@ z3y1hhOl1uW&WRj6cu+K-(+b$Or*`_bzf$l#?S)R>uc)q+x4ITH5t11ivia<~)Mxwz zjeT%*S?3YaUqAwg07>S4W(K{EtCv-Y-T%z1SvLOW>9R5rigMZ+h%}t2`^9&s&$!y@lT~(p zEk4rc3Gq#|%@Y;a>p`{w!;m8BLy@ zk5QRNnL8l^%qM=w<;Ny77&z=Kky+%z$3uk~6lM#Y2SPt-3JQ$FmH3OCs;oyRaBw0l zm7|opM-dLNq?JwV`FG?6S{)>~Z^fotnx5{WhQyv}%`B5#)vPX50?vK6t_vZ!-hMGO z2`sBlfBtZK%wmq6#Q7{B7y9|WPXm4$uCzdk_%ymIuVHRGKCCZJRgb-42&dMr`B}GSW4J>a-@LO4+2(di}?@?a;ChO*$-X8Xy*4v^=_|MLf(=NVat)lIIG-Jx^t;h zx769pvF-7b0$ML)(TO>Gj{5OV_X@#m>LKKC3U)0`7=kR-_d__Nc74oi%^#_$gR>=X zV`wCWYis(G=35Qkd85}JaH)X#L0R0WPxXTgx;&PMv1}(-o&Ty*d#W~hAB)Hs04c@Du_PantH$k)n}*6*>b38 zj3U%`jwu1NDYBX&tFV7mJD4fid9`3BsCuhK)-~u7ofy0|nXi-qV4Vv>QHu|;-x^Bb zgzp5YUa`H*VN(CK24Z-1{B(apA=(P^4Ujui)^q5^SLlC0oV!^I#B3UcT^$S?hmvn& z(y{I>{rZ?ob`6iiDfJ@5ZB-l2vj7>VLszz0fodJ|l@dSbZ^q9z<#h(gc^A7E)vrDWE zWU&3$rMDSzZTgg8U}o;49wS#m-q*67QTVDvbFKOXMFN5NAh-6!79vEo^H)Go81 zICp~w`XK-vCZAC6@|!iK9gDl&1O{05*GBN{ptESeYwRopkA`u1xnE}x+rpO$Em+I@ zS*>oTX>HuC-#>tlm$%i&rK{#Gqi*m&)h z@cL#M*f48ce0Fa+$s*Tvy^^1rzSQy+WOrMU^GK3=6;|5pGsC+1;^)5igRz&ur^&pr zih`d4Vh@M%G$i8Qm=MoX@4~GJFW3`Jd%SKFq4M&(^6_gLa#i`_R5WPId1bxXKzrwR z;oGf4ywTcuT5cGdJMK(BG~_A9F+Q z%h`!X>ID@kt7v2+KyBGSNq8J%w(l?ZHrnPc{p}e|+m+WxQ{Tgl4BMV$67*Ip9Q=|H zTvOSPydFc6VoG%1y<6Z}eSae+Hcf7mb@G;a-%~rT{lEiO?(Mq9zwkL%;v!RyUB5te zgJInDBw1v}`|kA7HTJbDz_xGyz`k{>z%kR85Bt@()}oUPndj6%NPZlv@%w!*oEyZ& zmH#@DY5zx*b6M(3cH;@67Abx`=^WUBH1?1*H#_@95B1 z|4I_%sB&pn1Hz(!w9zu?)v&Mk0*7pFRD=N6Z zyifhwMiMZY@&(edOK}#Qe2kxy&XmBcNkGs1Pzln(lSc@O$E1oa+2$vOnn~ustjfi<~Fc%Lqml&1Dl6&s@~u?1Z`G#k2}kkqW1f*Y5yA)x23qc6Hqli!>$ zyRDoYsnXGh(wyBD)9)$^Oi|J-JEQfs06Oz;>$xLI9H02yH}6%bQ^ezXz%) zk?Z6Z*nY{^Nw(T|xUKNMXXUA#{MC*hT;!+MA=Rghr7~&3L2%iX{EgB%CCE+9^vXg; zKI5uqhM^gyR@p@@3V-X73e-1w7^iJ4;036D;Bhd&-Jlf+=?6Wk&l+V*p$KRZV$D+sNe&?iPpd$SfjLIN$cLKo?NaEWzB|u zsbVs}N3PN+)Ta~lGNbg`{h$}~FeU`W2oNVS{Yub{p>*1(W>-6ZMw~>_BRa?Kw|oN% z4EQbF9-UB{?R?Lx{VKTHX*iZniL6n4sTNLuZ?H@u{o0VttU(quh5N$KAdGlVKM`Qa z2KGKzz!D_`D;f;xDe+Quu>cGJSgmmN%9#Cj%ZM~T)iM?*xG=6ST(BQ~bGgxp+~I;D z??g{(UaW?(p+c)ta*Tf?VofxbDbUibW>|Um_G|-AV!c6*=$1sjmV4(xJO4)Fm0Q^R zJB4nlrT2LB=|zjCk-6qqL6JwwvEiUdG6A*nmUBfov%$$go_;Q998E{LW>*>}McIQT zRI?Fn7DhGB5_AUdH#loukYN|i+sHm%Bw|8oJMhNwHof2sekUKJ>lDaRGKXn80mVZ& zJ=)%gOmgsFDOkqVi|W-U7y*kGmSx4CroKD!yiC3XkR%zAe5S7yUeJBokjlAKm@};SuWvDP5?DOp|=H~C$zpAlLNNo6q zpbBx9wRX!OrriE!U@*0Ym;4+Oj5(K0C<3kV&C}<TLbk^B)9jC(BHMkQnSjE%_r!0zCfgW+j^KNtFz^P(zM z8b@S0ZotQFLDu>`$CcWr`^XgTR2F4Ij^qDzk_qYbWQZD#qd7&_fkWKR&%N$#cmE$gGWcrE$=1ZB^(RkNF|CQnyR&pJJ zLP!4@$nOj&+@me~B#4G!$U)epkTDnHL*;gGPoEv=r#w3@Qup=rUMojuR<{|`al0+~ z(zW$koevv2o?sPTfHO)1;cNShOrQl79)jyi>(q$*Ym)J4 z?|amTS+E&)HPZXe8Fvj_p!bCy>VDdPUsO_gm1sR${UMW--{3d9gnNAuBoGR-JW6@k z6#T7cOkMchTJ)slcS=dxi5Dj>lx3d}9JvMa9=Uh0Iy?b4gYF5##lx|IBXS(wQpqn5 z<|+(v2G72LTP`V?2R_P?<45P3!VTsBGMnqce&~6BNZ^Q1n?hsGIbAyRmtA^jr^iyA zU7)Ch>r%b_i9A;SnU(1mKw93|ezr(&Y3(ogG!MDRK7Z-oL*aE;*;zzsYT1=sE6tAW zdp^hzxFOTo3h#JGkS@2g={GgF`s60lNx4!2kasp(knbrWmh#x5veBx@3;}%!^1npi zBHhDxhn|+bBH65lZ7ui6eKVWp)ag%c5;aXO2Yp3xL74PU5eCV^XPebtH^-3=ccv^< zLCW#BC^*+!O8nhH(VZ`JzC4rW_#ilKC;?ihW4fkMLIQC1o}B>6SmF(Qs{xdHf#F~{ zjUGsu*(i?(&3h^=uI)}ofBQ1%4DQFuX11Oh4yyCY0*4HO-!EBnE1kP=Xknt6#Zls+ z^PXrpfww~s_b|j}-QtC(o=poAh_2M{JGlSMwXPj&nx$JW)lEh*Vsqs_5GY)Mw0HRQ z@VT=rC96>i%A}j6CapAy5j2-=H;g`@DQu7oV9JYDvCXBc4J4sI*VNryy3js}^9&wj z7kC(-1;zO!hl)->wJeHB*qlOeZU(K_2iJ^$r;_Ji z92);rvj*XnpJ`n!yta{-(7ehlSU<>Od?Xq?^;tT)QmhHcF*w|HM$ z`HurB=40hRPBW7^#J~))HM2}m)z~*IRyvoK7GB$&0%DJG8LnXW9DE@Ols3WvKeXeW z(%`Qj__z0lW0muL8e{d&m)7_Kur@;mBlWh%G6VA=69Bzhb+D+|?T!(B*Iw?38b;hX zvktczkAiUK182xQT=ko3#Y`X_?*w`sV z3I>;fb|bLSKM4ChI_F$LjGL+{&q>rWRq5ZI*g%$5Ch?}(ZVky!29<-Ve?h!GHK%uI zj&A@Ffn^nvSv|-M0NRoFSlnW_d-A!HloM}fag-3~(nR^A)D&Uk`={Vlj=J&q#pxPnABL z03T3crZ)&*-0Mcx9euDGZ6(DXT+Z}@c>4GDx0U!s%Jqce*gtL$rKg4C(z{L!gn(n5 zA5Fx@SrSpAeG$N>$}=2MCX+~ItX#`jd%waFHQ#r)I|s!vjTafBJUJnARYVzjIG)Cs z8VYt1U;FIm6lfN;rB2I)2`eBdik_}K@r-x6MPf3ai{G1W{eqQ@j1r%cW#8SNE_LBy zU1)~A3*u#|w_5k8F-aCTK}}Ja2s1(a-bYP{yKI5veK`4I;^|ZI%CCyoUTbA$sf2IyZeVOYn7qz{SV7>u%MQ5Px!#^H+)2aW~A1gNtbIFy9WsG}RXPmn7b`A2iYFB?b){n}MWGIC4Y88?xzGR8{jfkdO;L)WZ&^8RU z7rA%D&a{8H{r#$=f|n&apKAtO*9y}PWib8@^UXhMXjgh3!AFAXt+XGoLr2WkfksTL z{R3Ql8Q8Z@AqQN0f1W!psKLGK?y3be>37~27apSDH|}>{=*vAP5JAnZnGfS5#Yjj1 z2EaXpC13l1AG6>6IRUl5wD}>jqCvQRDg&`|@fjZ5DPssd^+!pTh^+!F@Ty|>kM=0a z8AlJ<9Nea;)ofe@t%k|hkZl+~TE59!zHV(f>mz0J35HxNDW~5>yZejhmdQBwKElC& zRnDm(c9Yo@rsNfOvDwppG>G^~a~Ls;1bPlV4ZL2PcG{}S2nVfyicn=T!+2`|Z^+Wz zZ;#7?=)Zul1IQ)lt5sMwmG~Rolmqgti+#WklSq#er0J<>vq=j2CZEMS*bt81-vD@` z5&UBWop;xi8|oLWnG3_mE=k{lX>g%=dRnAi-=h4%gkm6>?r#O1KgQF-w8t%f3M+6A zGg6(?Vl$qhpkj`sy}y&PNhYZoun{^T2L-cM>Sp4 z(8n8@I@u^%7>-7I=uTZ_4ffTiqNFJM z4u13*k>#&c?gHR8!tDc=Zve&Q*Br-251sX-6e?mLDgJH{#+~iVm&SRbrj0-+2V4R= zpE$G6uDTFj#PO0QvTnHIUp*>r{htAC+)}^7At}1dsPKeQI(Zm+U;*34@Au*)hmP!| zfyXaC>OtBWTjqj&=w$iNZX`dgt1yWqTYmcu6??kf&`0>(4mQ_f-oN2G?YG5TSI8Sv zua59WwulF0iP;y>nKi*p;#0o!7vBC>u&hgR>+8ET66^{mz=#e1_V$lS?f_{&)!b?+ z0Vh-?)2WyI!czVV+pEf+8p6&9lEawkg^%?Y5I>s$(E5R-iLDrle+Xu&`xkiY;s$gR z;6hCoMa0o%7bx;5Yx-lG|9cp`MrlRimY%o|M$AAdJ6)0@RcSYL&;GU+gdtb%IV$`l zD7@o~6{C}Ev4rGnG?*ttHYm%_;nABHXY>M{Oko1(45TH8w!mSr{S$r}@blGVs@6xp zUdS`Q$pB1lT#o?ZgU9b15Me$}dl}p@5e2a{Gu;Dsx$frLrSYMQ{x>v=b5W%xeIW=r zl+W2`34b_mMbR$w5-T|=pN0T>Zb0KCoskjT!ENxs124O(XDH6+nKNBKms`06$*wFz zG~IkfLiKNhmd4D~NY&-F{SmNSPT{DW3w;0<5^_+|w&o@1MwE&22O#ciV-vE4!!nZC z@7|rO*$YjT%sc~KvMz-zV&&*hm6vwGwJ)XJ=_YN+sBo>>Ctr`Cnqe$>qqXj+fz?wbV1_Ag7gJOqj6QB0;bvgt<~gN z44?itX71cZdA1;2LqU2ytR#)C_1xDuTjMBq!5!7o-FJ~dVeD3caxj2)va!H0i%dYEhmg z-$+h$Ob*ME+R^^CqwwnM>S3qo3fIWIFr93I&rMnD>c_iBoc0}l`r7DE@|<=ST;07m zopB=#zN426q|iL{Cl3o7JP~S6ld3=z=ITdyaN}w6l-M~XU*gWUc;mfz=3joijXuqq zzzpMn2aQI7{20<0Saku4gc?v_IvUN7+|@c+5=Ik&&uRd2CTUz(IrP+6g< zJ@Vrt8rQc57~1`5rZDoNw?T zfObv+Y#S7}dnoGqGGMy zlTaA;0x&G~jYQlYjB79Jb&aUtU<|?B|DW89gQlL{3V~N8IAKk5EuL@5 zx7ek688{SY@t~JJonncDnB3LS4uL&Kwe!*qNSFfKc8_TOaC!6bzPE8p9eju>KqjuA zPrEVMVp*(0zbna~1Z=&OWXgs^SWQ>X&l3-^ulY(@RW)Z%qNMoW|4^lEi$O*>A%5%Y z6e}xA2a+vTIQmIK7e(fgGMU&AGWjrdmZ#*?wKkQO6+3*o{WVYzPhEKqi#mz-D`ch& zbI-Ao%jSYY+HjnFB(Vd$rWcD+(HzwkMqh^aDGbH2L-W$x`_xBS`l|-_Dbp`IZ#cmBSZ+qm^e7g* zEgSFp(+SGR0{nPdf9C7jU9`-U~T?a6qthi=Ext2WG#TazBQ>(b*+dCKDM9|GK0)54FP)IYc*R`z&oW!ZeP9;Wk)0o<| z^&q%N?fu$4#Rm@D`PS1YW)k82ba{HXJJu4k(=HHBH^xb1TLXgD$ut^77rPm{&;sw$ z?AsRXw`<^|=Z_j3^V^WnMW_s?uEla&JPqhWW7UW^>&^%dJf-!=6vvyTWnb4 zBBOCZ8<&nRwd&ft?`aK^m*oEQit}KYR*~L6i}T<>#jcxv#&YjZZ0^fm3Vuc8p=dTt zhMTvNKel7jvwP|E7{1IcKif()GfDyVxXYzoBW>PeC4n4OutdWsBqK+n+S0cs3G;Bq z-W40uCcM;%a1h)z>PRkeuNJGozVzTxF!DKU$F{SYy3fs4D5}?d>pxGma4y^=`TT@t z!>IQ7r)x`X?y>Pqo7!DL(D+nG&Z6MYZ1hyTUZ%mri!xdj>+pE5Cw)LOCG%!j%XTS^ z9!ZoR{!lry!Rs_d{>Kp)br8MZ+r59)X<-y~Y$W37y!DTvp|_cMsx-}?XSGGyyWwVeUZnA)Wl zlXA`t`X;w<4pIYTVD6;y7CoLX-8s&(0-BW2bm@EO(tFP`;qUO`Ut=V~bJA(PaN9|S zfTvL25*D_m`Uo+p%?Gq*MdGxTx=a(YW(S^RMZ(Y!T&5vx^A+^IDC94Y`X9cB++qw_ zmdm#c?6m(%$fr??Ys(L(=`4p)(WGIM<+N(^XuniGVRp2sQJfVtRs zF(wpfn(D+cKi)dwOwd_*`aQNO&k^R2OcAN48Y=Xt*}j-1GOA0gEw%!DTl;SM4Q@sl zqq_PhFfBJD@WV0O_l{LP^bdqsXS-*m5=cQ&fQ zUB0CR2OO@hZLW+0cqa$v0TyqL+Aog_6?&)LCdqA9E4?P4PW#-@d{Osp48~8hdt%Mo z-}E=No*7Sav^zj9r1Vng$(#1?wf5|Tb}3gA{&e6d$bBw3x_0^r0XzuoY1O^W;0C$e zh?IP)%OQ>e`{>%wjqULX6Ka?9AHNQ=1!4{L0 z;<4?_T*+EsZ|N@FN7vQyQ60i|L)I&hM6ZWgZpgqvU9vps^WlRmgAny_&jO0?8rjq6 z!&AQ@N+v_-v+ACo)868<_x^I0{Yk`~pgu>vAwz|pc4oVBmGj++MB2lwBlb@s(w2Ic z^O_HzYhkF7az0Mo_|B7y2`c2>R(P-*xdq4cI=vD zI4TL+&V;6a-xTdZpGuokBmAV?lg7~W_L1l+WMH?#g437) zgZy@cYn&(4vZF>t{i8iW6C9msIyfUDd~>5N2J|L9`*iiCT928;?u*b>IIgnT<6Q_Q z1=}XZRZgDrekoU7Yt(V3)h1??FRBG&Ns=NP8P`lvU!-JMcSPQii_(WnYv z5HoPj5^es!_Rjh(%C}wj5|Yv-C0(MR4IImNpQ6%7s4P*N@)Py7v zK%IP1G?4dFqAViQDB;_@>vM9G`N&L%Elw2*xstOf??e1Jm(Db&oJSorlOOR}%wIt8 zsRDgUC{&%?v)n##7TxxIIeUXMzrsMODf%KKJAt+&kTjIF1|}-r9q^HNpaw@wI=vMcMl9cxCKswXz) zRwtbC&5>pqta*n`p&G+$6>}%*(+T4)Z%?PkGX`ceVRMB!}6$7 znmw)YD_#l=yU%DEO(iNsR+F@X?~H%(gLqrzfCc3V!`{A=f0IM(6iK6Yen~djr@}qA zt@ZT^B|=9ykwPHRsxvJ9L`19)@@yjdISY8IxXdcg zO3;3VSfT@>=+#p13o3+)K866#q2D^cymyN5bp*B!ZMWE#DaX)l_dY2V1}EFcdDi1^0mY{oDpFSim90-0DM1-27E!>*-CQX4(2AM*~I-Els}> zg)6iU;bZ0_sv=J-9n&EkJEBde{&<&wBpELok0(KoV~lU~qpREe<@?tut#U+-Q=^tG z7xbgXnVqhlV&sq%^4ojhmgHVQi`8eS{k;s-08pDwn*W47nX#U5&kmo1J$$_(p_^xIXU7L zLDNMP>|`PFr$oFS>b=~QXj2#pyLqE*hle2!QZ215Cwmr7_t=l)D#=mn1nS_&&D7kE zh1R{vgLOdhs3d#e2UtfLLQ%$SKxXMs%3irC%&b6FA4s+}k9lYc<# zqvxP)uz%%7?H~cvaIHV@T_6BbB|IE(yxBg-%ftUX2$c# zPJY{kX>_tX_rVon1YdkcShGLVi;lYgCyPIu4JL6!nCze_x^QkL*rhNF)NmuOAP)03 z3t|H)(hTaR!DB3$F`~AeMv#uDEnJn%ndm_uv!m%tpK?Z z5fRA|sNNj?tbE-!!We6Sh&4O1me-*XlR2Wls$tw5_(~H+lcquQG{PqpgQXW!yPH>9 z26Y`I3DeFSAaMDvB-#L-L9OETbXr24X!A*eB+!avIefM63dY>?mGaOE*$5bCtclrY z1a!s9$QLceR4ZAMAUb(RpmfFf2scegwBZB^xdo6dgr}W1-EnO`dcF6%**!KI26PJd zQde7Ixp)FkP{ee2{N=SxUH}58L&@Ox^1>bm8TkZEAktkxg*%Yfhx*rf+d7%#;|OC7hPwG|5l-e~uw17e`pVbR`xWm$ zk(5@dF~Lp3a3<;bSiM902j57L+Qjm|_vxdmA&mQgCn#hax15cWhO9dC{ zeqjWC3`SxVAncDLjb88nx=fo9tB1iPc}=-39^%4xymwQIZeKwcSSXav^UDIA-!Z;)2zQDd zw`VaP7D}_w_D|Hq* z_-Lmn1#|Gi)a2E5C?w8$0{eEyv3oNbcJ1Ss1nfe0AJ~H`J@)|o6RwLqALpEE&k2pk zC|P=n7gCK@DQ4WIK>4n2?qGG61I#(QX{N*EiRS|1(>yjzIJcf#V7m3RTLb~(!@eAA z2=?8TcY!xNm9gRLP#i*^_ey%~WqB}k!xAw4xVA7<{eG(tiYmM}JZj-8aaYXu3A8q2 zBI`>1S05b4`S2Y%f;bHS{f7m1W za@6|jS4f8G+ovRj>2;p{5~z^YA^wFqt4l$*9o8p3qvNrA^q(BAj&n%zG6B*o3l4mg zDpy7_@?E2oxv}sD(~NlFw;Yln`QUy!J;9oI%b?Mm0IhdbzyphTM!`~}Zpqi06niIy zcwH8?<)`7ek&zKZ6@%}Kk$3+zfSZPX?{%6r@+u6=+^~&QobZ;3>~xc7{+;_I+i2M9~yXc zas15?v4Cz&wTB43z!M2}w2%5Tqj`+R2X8K;d;+TrLp!;Y$ZjxUJ|XK2c5|OVHu$19 z(mDnUW5ex=Kns-+y~HFidec1hYan&X)Hmca;1}U<7I9x1u}k+$k*Wi``DeKF%pZN@ zUl2*lC*kh2 zp$NLrn5CQKvYNetK8NrjE&nt=@sCQvFo&xxnN~bU^tyYQE=)JeZ6ordM&;C6_3Wh( z4nxB02rj*9FQx$b@mXhYD>wBT0VeZS;FTopEbo=iu204B4%g!_pm-VW{OIG?Hs3;~ z&K8WV#OF6{Ubq+U=0`D0LX?skF-ZqZJS7Cvs!#b{cIawPM6!itXAc|273>@A|_6kLYE5XC6Oyc%)`RzWl22t(ur&2RTl8Rwi3&>_l1w3Z$uv7=4U9p zM?Ip?2uY8e?-uvy418=y`?-#Ni-`o@Pn&%dD~HyQ((s+6M#xe@KuzPZv-}Ol=%1Y0 zetJ%2f_rA5Gx+?FVyApRr!AG8b&AAeH_E;f>@raF zY6tLenJ}M!l-Z?h)Q+U5fJm*Eo>g^yr)1+>W5D!iFl1&~=P=kpTUF+{;XHTrY-s$o zZM`MhvuDD_(N}!}y?Yyxl@TGHu&l%y2Y;X1ne%k;$rqYWl3bDbA0Q}C^R3mu z`_@Fr!yG~@UjOR`$kpu|HyGC$`FDWnGSKS&%>!zJ@kIa?K`_iamH&r3e@S#Fk7bTg zDqpZ!eq#{ZDOfJKuGJa^Fu+cQHkgNnh<)w7z3kF9jU2x}|nuuHXO7 zWy|V|8csZaHs1zc(&xP}0-8aj_Xuo8>TUcYX-c9fZ1D;!6 z)RxT2e>=)6R4jbjK;F(jbtNsj!&ZJzx)Fe}eHh{6_r?8xaPNMp1-!xr*7ljMbN|PI zo&@~=kN$6?^#600N!%#-c)OxKNt&Kc-{xzGcG=r<7h|i?`JpTkeZcj_OleaY@BpeO zSDeWOVL*Yp4}RGA!d z;#U8*wqY`#H0I> z*Gd#WBBAQHBLN8TPOkzi*;pX-z(CM-i@1J9Pk#@g#F_YDT&r8~^?s=7n;Q4d9L=fR z+T{w1{ULHUoM5+SCd;0qJ>&hzezD^pvj%vm8%?E}YoLnh_VMM4U#W~6k7ydN4w1Ol zBgvh1a%)!$`Jy5V3@O<9>mtjcld=DG@+iYp44y52^oZ{6fdhxckb!kF!*yV+G9o=+ z^ty{2ud8;gvAN)yc2mqLzPb4H?p)0CI<8K$Trp9xu+w6JkbZxb zkX>d${UL6$_G~`OkJu>w8S%5(a<7)?1d|fPcrsy&vh~d0i}nfWVZ>!%M9$FbR=u=W zz=-R$OO5(>v)PD0@hodG?RU5wrMnTQYw=pRxOK<6x>~``pTM>w4PTR+P{R{mR<}Lb zfdtqXI*vg&n2PlYZO7I(`A_1TYU`5srm{&htW~}w6t;bd*sgb`_y;v0ZU;;Z+AWiz z0e*J%u0u(Dz_9ngu-Q#Yufw+-2T;kOrW=4|oE&!Y@3E$=lYKuTK1rt`S$AOErstnnXC^ls+W zfpO4?{BY63h*iLdLZ??7tLf8`X4#o#{g=H{P+YvWJ&wJ&XqLVu^i>)YYZby_5BCz6 zgsuPBsN`wm4+EDG;pqN_?C*>hiwPY4(=c%K5f>t*+1CsihkNo?etXQS*_`EeKBI;| zPRnBwv?z8NqGsI_vwbgBaOd68H=Rz|4mo(F*cdX%J20u6uBXon_G2*%0v(bH+k$DU z-u#{XSqm_T*_R9|O#(hY6VYQ<&0sy=7)b^!of_e0-T>~ppcR5BRkt`=?Uxjc3c)5* z1bi@NfVvy{5TpPQ4t&yrzkcW)0Nh+=K%@-ZM^keL2qr+l6ri6^HHDyp2P{W>fCStO z*kZzsfuc(yP&85iG(iOhjZP&K&^jPz##zT62(>u^k|6}1{2^leqMXX5V6Me450$7f z*zyHH_?oR=uE~WV$MIgwZ`Oy>Ssr3WcB%$BoDd2ujol{qXS4Q$fJN^2rQ2^;9jlp| zlFA=)4RFr33&}#&%;$hL&uK~qD;3b&RjDrkhUAA-C7`LfzIS=DITqB0P|KTzx8Ge^ zHD3d2_6EyJz?4RmC9sEg-UcAUtA4v}sxUZz%)Io*9G&u0Jv|ebT{3%g+tFoyc%l605o**wMb};g%uY@;i`;YZd7wT za(h3q>bNekAP)Tg(ED@zp1m()WYUXd>e9Cfq$=mwpML1qXlGG_RNa8`%r*mhTMC~V z+UGmPw#@5%n;g;XW;}a${muqLt$|)xe zogdgSJeWZ|H-D>{|L94r0x;C8Pj~GB`;CM7<@q*^pMY@Duo0^?{dYC@dtkniV_hK!~MWv#$6xMOeB3VI{*%WF_&O?cu9!@i&Jn|UhU z{@>hJo>MY%VQL??|9-pF6$x1Rx-h@C01-t03b1i0!Iwi6!s2`P+wbQmE_bR&uqNK4 zqj({}Hg(e<6?*#*J%D*zuqxGMaUtrr`+}hzz*qPsz}p>MtzU2FH1S3UQ$SYhvFU@~ zQFfgwMBH%)@GUEruh-GB0-y$c#={_&^UX0i01kke{E@jf0NA1$z#G>o1f56X+D|XwO>E8qWxH*wO3+;Yp8sP`0Q^uTfRF2XP!T`ab zWgdutL4F@ih;apYDK`w4Fn^g+ z9?PHNNgRgH0VnGK`%6WR5yOkNY}ZL$CxGc+>3zNrX$ zr(TWLJm|BV*4&LBN>s7AIk|$0*F|++HEa(;5Zh&+?v~l*#=S2a5c+`SYYp_XIojH@ zA;o5xT<`2hDAK;KzP#inZVBgu&;YJ|Ex7N_OS!yX`;QmKs$<{`IyIJm+#e4@E4(KO z7RSYfiHb(FhV1NhqD4g}-L>%x_=~@=qf$2Q?Z_gr;Q*;PeCD+IYOQyl1(;Pt|BdCc0Vj;sLUVykrnbiARF?MInluWdoL z3AsG9yIxUP=(iQjeWj-0m5?zCtJ@9}OR_eGv_618(7o}J7w!#~+?ig|{hDGAUAt$R zzB9Ra1_A6r=@%Y<3JC5hZQlAakWZ9LRob%umxs%XfR(fhlkN&2gFMc2@1qL?+0v#B z0OW^GtE~nI+KwDeD_GWRI70xDCxd-?d%jShS)k|yXzztos8Lo37-Ugc0&uhi ztI(f4xe78*E6=y5I3s?F`;%Yz0h$3-6s#oy07miH;8$4aW?4m>(=5KfHnI;8;nD}J zt@h-R1XMb$fUKvuM&}JIX}l;VM60*ET-Wc=J8UAnntn@OJkbjice|P%%SYa4J8Xl= zobK-&?Pj9b{qAq;GXR*4Dz*Z(Jk##j*+Y;RkZVQK_X|MRY!0um5OV>q*_W%A4}6^G zZ?uD42H2`*?V2$+0#}`l>GtPPYU%+X&XcCd$r~Vd$7L$$d#bnEo>GRl|C?; zCh~D|O7Sd?=$GzFUeDZyj;+R{WBEcisCA~Sm^W@4w6pfa_-M6DraBbH;<{cydAQc{$Lpf0s7ndy)@I??|?&BmD@0KF3P$kMK;@^qk1oq2Y+=_1#`=KQ16 zyKhr&GU-#g?U&#iGEes%v(~~2Sa(U7{Pw!*s{Ihj)v9&=8?8o7w5_}BI#o1GYeJYC+2F?a*o-)1cYS zs=(3r@CZ1bvaon;W3o}#`C^+=+MtTsCU=_z`Lq=8ie*h%@%r&FcKYRV-QOK+0OGSh z*zePe)9ooWRyJop;Mwso1q|j2wk7Kfg(nvFn_)k;NhIF#^2dgXhRVJbFf%d5?Xc%l02>k3A}&8L)pcb68q7O!X^oBM#b zXKlFuxY^@yS#m?WdOJAnQ8O11XJneYlIpg4JXaz+n+XLz#(%_fG9*T$*vBvkn=R=K zB=oNLpdH<*vctUXXX@YXW@-9BMZV&j`79u1U;ALEv>Lx2A7ps{~w#o1&L_2ty4!agtfVJP$tMR7qwIo~vYl1+h(h`^J z342Wibu4TwMW%|?Ap8CK)*#>FB%#KopGn~7h61PgIJ-k_X(P_PjA$VLQF4n60#7dA zWU3-#$HP3jK03yzb~}5>yW(AAzk6;Zf)$Tu^gI>?=8-Ep%ayhbY_;GES?{(t;_;SB zw`{j|^(VnvLyFL|>!?wpkEk0BfB@Oo9YJU+Gb?9wWQOqNLl6|bn%02+lsx@)*qhCc z%ry%Tmaq~Prp^v^M0T&B@b(ajD|fl3v#vFfrB$zd47UcGLf46eCegjwDlJKs6l{{H zvE)cV7V)CkeXl!ukrceTK%MNewK4pyr$bH259d?JzC?Fqmi!AtBM(h3R-6C&jx2q6 zN0!{VS#feHbMfG-&N!8zclBthyo2JFSrt*rmu-HO9HM|mlbCno-ArWgemIS$7HT?{ z>}e^!FPZJ_5LHIIb!-pj(F_%fL4hHavtDO;GV7%;Zr9sPBNDNR245{{S1TIV{6rAZ z*SI>$sQ3rnmFn8hf#C*%U7LqOXTLsh*M)^ne+XHh8w8gBR69C;(=||(Hb~ork>^_C z9zm<1DZ?YnvS^7+z>0q5Sv?)GvZd}`di_vNVIwFS0{D~FTPxdhVEyUK>jy6n0x z^YQiy^qhsW+eMcrjT#9j%G__j#>_S)@#fj zKLBG;eP4Ba_NO9#E0|xn1UwE`@~GEWn{w+7TX4B88EbN09wHzjV*j9;0kh_WEWcW! zxcvm;?W!)}xhPX!90>zah03r)3mpdT2p(TI_J|GA-s>5Pv z1J&q7i#Ji*-!A%IEQMt*u*F#(YRDXHD{qAL9x^E+(rsQMUIq$3qkR*>5lRAf~RpB@bQQM`FcJ)TuNq~3P?!v5f zc#H6bbG`MPGd1+)xND88K;}?1W7bN07AcbxL&tgB+4n*>&3LrC+}LN5!0B!EEq3B2 z!oJ4EZ-t@W&m~4F2%~Z*e5KpdzWKxIA2e@ba5D9j*kv8IhqSit46agBUHdpjlkMIg z2C(VYC!KO_iL}yeGiVbKBe48-i8+39446w-0a42N-3pn|{m{W7XnWB2T5JB<&4GeJ!^elth~ozJ|OPjLE~pO zvwzDriZsa984jiE^78gwIVm|}v0P|~_Xe z41<~g(v3V%JpxkR+BalqnJu?q@FX5cbciQ44=^a#gtweK9r7T#ulB~RTA!yaLw@MPv+9H zMw3IfbwULJ1|FuJuuKBgnAn&93FHi@r3v7b1<_%r*yKe8FlOXiJu7`%PC;AR>5(D8 zBm{I*aw_5Cd!LAY{STI!vOk%XS-f9+wO#T@W)_des zIP#`;-?o6(SQoA3l}FOiH?rg3Y+8jNZigQYV+smH!^ImMb7X$LPI-Az%p1?GOyxC# zFWn3}=Z?SVW9?y=@JG*y9W$bQ%&iwW&KhL#JZKYAOfFNyRh8NMMs}wX5eFWiAVws* zraE|0jYnyrl@%TWMqb0xL@1i(ZS+60T2!^ZfM=WKc38T3ji7n&p;oP)u#UJUS*{_gx|U% zy`_2+a7~ke8v83u&U@j~60e4Llc)OkIHUafnM-WJ4Y(5o+^OZR~HShWHgZUODX~ ziWvAzxR5HA5jCy$toX=1-k?wXl-zLMkz8N!w`!kYg!&VWRQHo2DB{@``Zw?uh3DO? z)PWEOBlq8~b_YXTaE~s9li6Z}$eR4S&z(7C!2l6syllbS9AC>=Dh;Ot!8D?RNyA1o8DFQ81O3;K56ZJwcIIbC-9;%>21+ zt?&w8&0yQJUXs%SY|YG?7N#q`{27YN=~75StPrQO?>oZP_l)Oe3CiiMtd7C59;d6! zhR=pwG{E=P!CJQ#)TA&Zg~Ty?*1kx^d4vnlr;ongYJOxtK`+a9S|fXNdd~RC;rpgi zdoEERU+8D{jPw*DD4zB$zhPq?xl`^(`rDS|izx2bA58z2=dJlE~X z2om3VZ+~MLo<@CYRfQCk0qR)KP_7(bZ;;h?$d|H-=S;tv(mICPY)Ohi5qVuWou-V@KG{_&t#ukto6&x$P>Ou24G5_?5lZb(0v9A_;^KJVi zElDI=gVXsZ@51%(aAgfl3yo|cKc5%<3fqq63dYU^YHqX?6*$-QZop)9?eR|*7aVm4$BK0FqNoqSj@Ck_J2F8fpZOx zgu_P6|CwO?>C~$*zPzSUrH{FCA-j;?1Hi?@$r=g?*rd(?CpmJxftWW@`Dm%9K9;pY z@s9yT+=C32RthzxvxuN`0l&SwVcBODJOwYmvt$nu#rLord|6M-6gf;5TPNQxSxJe= zhGF@9i-`pkQb_thRhN>?g_+7#r$6R^jFd@Fa*J^0KoZs9Dvv^k`SF|`>f(LO?|>*-UrLgd7!G zGhn0%kM|DC=X3`W>%-%X>J^BW0hnU{rRULS_2lF8Kcn3RuMb_{k&+m1J#QqLNJ5`d zT#)AtjCsxG;Nd2(Bq?ot1%su4zrsLSLVonVc(89vx(v5CR<<|J%Y$ZmS@6!lLP)*( zfbF}tp`z2OeAAXe%HN>TK*1ECT(mE?1qY#9`KmVlnQ!CLXUOq-0K5Wnei}O>u~8`Q ztWvmTT~

p!(zn3oumJ8YkQu$kHQ8DPfHOr~X2Ke6aIeS0~+diWnboy2A zi*;|6CIvenm3dU37t z-G_I!z`C(S_E>_nb|A9V+nbJT6Ceq~Uz>2# zc{EQZS{r{2x+q$TnTW$DFNVJ%*yOUgjA#jtc)qEs-ON^+2{m+AdG_v-qvX5#xbY~# z*HnrdG~`eR(N}aVjM9P`8_urys#iH_sAQ0jK)*|3fFhjC%-Dx9L^x&17jWg)gnfSc zT2kBjCD37lt_>zP`=GWWWk+%}72{jM9`&RbcPPL0j{|=<6nTZbFv!vTV%NW#^`hzf zX?7ZNv*kePrQ3N(KL#^PANv5~qkveC(3Egitfe34jrrLoQUg>@F`>@2SJr(sC!K+l zV+#wHEaOlmRRNIJp=2-V3!}o$P$PBv!!j5eo@Tp3qkhmh1mu`hX;k;)N>hm;k5ACV zVkq6y|HbVoQ0yJQKNeF1A?=-u>B#dBVGC+&#r-gB7%xbCFV-lmX4#M&i5T!oz}HP0 zXYs4J>7|APrO?LJ)u1&Grd|AB4OSZRGLq^=0It*t7XO{^g}yAsvr4E7=oEkYZT>`^ z-vRIC;xT!gCx|V(oHIc}JAD&bs0}0=Z@c)EzqR6?cAv6^t)${WnaQ!h5diT~ znd|TiR8gt+ibIBEpNZ(dv@i`ZRv(ZNnm?E0+K^uoq`?8P?CSWJw23ApbUj2v|L#iG zqoJpIc?Q?g*WmTdn#@?JV&d~PY5z{tc=R~>p-)p(-*?>=lB9yLM_1B5VcXBF>M4W~ z>2zQ?)sD(iHf}H{2Yl2xWCC;e$T(*XUM?F!Idu{?qEEyv?>5NoWK)2$+ zSf&GG!|!x>#tRZSdk@zA^+LU{~YR{qjuoG z(*dMRjqo|EX4SvX&_8(tZ-E3nkPi2vo8bTO%SjR-z5vedJ_8^7AI|3g%2T}%lTp27 UndB~Segym|$f!!gB~1eV7vu8VrT_o{ literal 0 HcmV?d00001 diff --git a/doc/op-mr-opened.png b/doc/op-mr-opened.png new file mode 100644 index 0000000000000000000000000000000000000000..50e0a57f2ad693f76a431832e4c3c1dcb92a905b GIT binary patch literal 70424 zcmce7b9g4p@^HAZH{94ZHnyF-v2EL%WMkX5ZQIty8yg#L{JrO%t8?z}?{A)`r+d1q zx~prZtE#Jd!W87h5nyp(K|nwdBqcAV@M_waZe`(a{l+GtxgSudYBOn6?|0 z_n#lPKPoJ)e=E_0C{XvG@KAAJ?jRcT9EFoXg5)9+`6dKG8bUr2R#FMViu~q)G?Tj# zhb)n2Oy@yC!`zEu!~@|;8;COo6KEEFy%Vw}j}NIl?h_nD3rD6G{1HO)^BS2itk%ZX zHaj2$xmjd)2t*-v0W|WC|6uY}`rX;Y7m-4FEWt+>i%)y(xr~ zc@#`iBGQn5|Mk-JD2PfUk&nVLJg9Rl{WEQB(4ah%+G!5+5RaTds-w+x<~7wc@~65a z57-|L%9AOdhUMx=8N}Muf(LWhhH%FQyG6bq)StT->Ghh?++xD*qL#EOq%xX?7p>MA zEHJ%G^@v~;ROT?iK4ffhNUVBQ-^sthu8ucB2DBoZTxFg-KM^lG%UQ(Nv*Ys<+H;ZX zVl&WRD_@dLhbax;It3*8S{PJ1+JN3T%GE?EWgB2nuv&)uBa46_{+w4Sf&K>OtLUGP z^1<_qIE+zQLBNctB}(2bqV=KKnF5MJ9OSc`+l`C6bVh3e^2KLqnm@_>4V68BdZ0M>qT$}C5MK{BSana zH)bR#Qvq@GhoFTZ(}$#lO40}a0cP1pUkil`64IwQ3o94U@RPU|*0a~<8f3JO&>d8) z*NgF!ATnaU06+qY`THc{mr)o)0X_x7&xk_;V+uHlFkyt)0I;g>)OpmU7;6Y@0{ns$ zxec=>rI_td_re3SUyfNK1IB8}E0Hn=8uUL+1`PcC>W-fYb2KnykKKt_?SHl-Y!By+ z(G9yDd~glt{uxq%^fQ9V_j+L_h4)ek04y~)nIN60N^ZI0eJMGByBy91jZC~t#7D?z z4#0|M6Y?MwlDjvj?ab#%^*PMyP? zqAveK+MGh00v>fZ^i%{cpMxnnRa9HBl87CJ0hs`GJ6?j+C?1L`os^W0VktH{mOk!T zGG`RW*s7kNGl`a@hUALEE%qvITZ!_s6Kq1f5LIqv0jt8aBEB+Txq`~M$`5LuBok^E zYC38TfNmKoU<#m7eyb9pQlS!4rmN;tCZ@omcB{5mN~cnx%$>Jf!d@{^6d6qKzj4kFiLk#11Btu<{q z3{4JBj!+FyjZ@89rlR9S5M?Q;v*2vJZ!Bo!Zk%DcGpjYdHmgjVW8E8$L|S8MN?Y^3 zfW0uiSYOL=Ip*@`TIKrUqUnNvlJ8RGQs9Dldb!w@`{G!9C{vTRk%rC^Gu>GOb*6DP zbWFFrUf?Ec2iO+iSJ$iTk^RVgmj+JeIC7@ripUnO z$;&B;!8*YX!WxTo{o?W^3R??nt*Q1&zErJLiu4+0J2ry)*=BvM&swaSJdK6w&zgr?%UWCY{hCab^;NFw_v!=7 z)yrUKTxVzOwNV*Sv!)@kHFU$v1FFVacI#&1%|@%}Yiw)x>kH=t7t$A67kX37SxL-R zdzIB1+ql|zj<^q8JobzB@Gi72)^6T=#utMw?2b;%B6;Cnkqb(4FABNXx+Ghj0X*?#V; z9wuIRcR1-bOd0%MSLdL1Mt0GUwsvhun-o(=Py}r?aSS6ZTaPdc-^YwDjrfc->gv@) z)N5XGUrBvO-uQldJv02ef);>;fC4}`Ls>!$LoI*eLu5uAhqr{sfpJC9#oa&&fPsNq z#hM>kHgj4LiOmQKElWyVl+XVze&Ia z)U2EFd1Ri<)ue3N z0GOU&)6b$=J#`xIEcEy$nloWnl=qBG%v z;eOhYx|2E^8is)S{6aq%tFgs=sn3?iK}>ecNow6^YX0FRC6@YfU&^nyu|#QMc(fR^2Ms)YSkkfU26W#)OX7f!D~D zRX2fNN?o;A*ZOnTm)b8PUoNfhHr;cjy2@KE^_2&Ulc(;c&@;AeG-?kVo22ins|Ks; zt8UV-)0=II*V(OKlxRx3bdJHuU$8OjBTtoO8coDRL}53`?Yp(ySqtu zdHCGppJ(^;%0H5yUp&YCG8*99^htW@zQnvpAqeHP@9yi$+sWPPyN|LjsC4+PaGIAr z02;EMexB~iv5-y^^NpmQWX4bG_IldiJv-N{Iu6=j?wj#r zvLCiqyGrbSZ+-P*`FS{`b*`mmH`BK1Me#np*}cg3q_ee|=f(M`yUoza|DgXQJUI|9 zZW&WfaK>Bx5wgUeeqei}{092Al0C@h!PtxE{ww3Ig(1*Ho=UzV2b<3d%Mpi*6PI=4 z*8B3d4|5mOh+)D{?ZfAVWwf3^Tp%6$F{2x zK7jY)llxD!J>`|7!I_z14BtMVvM0;?rZ=r0Eg^etdwYh_-&u$t6-K|YFoU=^5X=pa zfheHp$YpYuRZ+mFzw$E*{^b8v_@2$k0g|2yB0@5H4)QZqXqtGNYE(fY|10^otgNIx zJ@P4g*YrtIu|%@Nz2CpJd_e3kK)jP8V`46_2tL{|sUGk_@SYOVbq&--COEK}{kGFw zD@x5EUZ=?K-*Y@)U*JEZw|?uH`GDB*!|3+bmK+3{y5T13lBTk0DYy%Ng7M7F*=E_EnCMLE{=624zL2w_y3K)9{4JQx~46;8GsH76 z3xK+_x~vSBk(~{_fw7&T3B9|G{U7~+@VIjUi#8_C288Z5*0xSu?!3hRfZzg_|EOjl zCj1A8vlTC~x~u}Bu$`j`Asam_JtHw6EFmEwkE5|Emy(FsKhc3tyu{|t&h}gk3~p|2 z^lmKlc8+EYOq`sY42;YS%*=E^2s$SZTW14zI$I}_KRfwXKO!bhMvfNt&K7pIgn#sF zU})#!%u7uC$3XwS{-o2y-QqtZ**g7mTEGc1{L#X|M9;|Z@4kVkJb%=3DOk9hSgVUz z*Z_G39D|ROk(uWo@c+~DpArAWRBfg9{ zog=1ScG@p{<48tQaKj!hO&p()l?2!>@n%hbO?IkhA!DH6_V70`JB`7zG|M+3HsK^b zP$GH~x?;XlkF!l5=8BI)k25C~XRa3^P8*6ZAX`8=mMEr8j0{g!O4lG-4Z>F1GA=Vt z;#e^9w78HrN`mzXVDP?9=zJYxU^aN>@y87|VI%Vmx$3SvZ`f?N6|nbx2q>yw$t6-! zD*slI>GT89QRbuDmVYVcLcUubgLclW;AR(LL?Y)BPpL|?Xv1`56Z>r9OAA8cWtNs2 zm2w7nPjnW!xXzPIlS(?Z;D+;s%0cUU#+aB-yQ_i z@hMu8hYvV+wZ;`F$M@@UrH*jd{&dkj(m&D1An+Y7#=JcKfZzwhbReLzBQXkD3ASgD zb(d}0{z)^_IKT4I_!3eNMb9fQUD0J#+2BRA;L8~Gh1U3tQSmsF8vmDymdEC^QJ0c) z?3d|jvcTa#ESRIaV?va!aE++V`SxNBb#08`ca(BwAH@y+Ll!z-oX=az? z!zpe#nF_iJ>%XnJ!hmli9uym7U1}B-4BO6?74_y8b=F?X%<__j#zlr2S8%fB2alec`%^zg%^X~}_!Un$tngqu z_3U4+H(63!Jsg)Jb#*;(3Kvi7lJZApA6Euu9{pHY!KFI8JKd*^pwNEoBnefY#&5kA zShbH*p*)0`AYp-Ob<6fSc6H2*AEOi_JkKFJaGV)=>r)h1%oyeV_N2F0o%$QT?h!24kH{ zwz*`wUp+EunwOq=O=>me5@0aSAtT_5RBp*7uLf2&Z_kM=>vYTqXTc4%{-R6$52AYy zkh*B>%7&htj6Y*bBcKHQovu&Gw4%=kh= z`1fIcJ}6)uHnX09E@FJpNV&&{Fcx!vS`geA%_btK+|KW<ZcE)_#>kfywhm4 zBPlMUOT)I01BL?*1TYqdVQ;ccMT__x60PbLWqK~tqg1UDD+&Jo!Rto{mi;s&vC$kf zQ%=Ly=kkjK2p6+WUdV0JwMsZ9nZ{|9Ns zF;k6@6lnvGJas+m;MA+=&6$OlL}8=)TB1d2#z0JxE>zC4c_@(xqE%vw*UY+J^LlB^ zhh~M%hZBDC7z<`AmoR;`ww~MBLeA*IRkXyK+}R=I&1#@o>+M+Rj`G#(v4lZf@Q^HN zYgM=RtpnK39V%(?y7dnl)1lj=H`iH%^@%zh(MEE~AS}nL5M3_TLn+w4!=PWxZaYex z^2&~puTJ6o0F#a}&5CgwvkMX%P1ZQFXH;9CU0#)O2qbOqNqWa)TNUsph>0J6x20mK z!Nhf+8fCJYkj|{xB zx&1atOc=iI`0XlM&FJy^QitLMr!Tqgs%ceH1ir*vA=%C^gR%zULl)XT@O;tDWmS{T z2Rblps8WL2@OWye)o-_?)AwqcSyJ)`P0l+`gV64CI9z24A=Ca33>*m45?Qqswge5$ zD@^Pn7Ch)D<*j8e#CRz;;yV=b;G>hZJ~^9=4yB0XLaUm4}$!la#Kz|HOOX4@aFM9=HAa4UWL=`8CVzO6|aLM+R~SETd{u zmVTirY?HeQF-Ph*uZ3XSVCCIB{|&D)XMGmZ*%U2 zRhm*cA$+mn8@O-ybLk$oyoUYRMV<;ZI(b&O4hC7yZ!pY$gAD2HB+ANaB1Hl(b4L$m zqy<)XV`GyEN;-k57aKYanH^S$suQdQ`NeCpq)O{-!IL}!`W$*S;<)~|C~@X!DT_7ud=WYn zf`;ysx;_V)18(ZBVu46y5r^My2_o9MPz8=A16!i993b;US5T zI?b&``kUAe?G3SV9teoRT>9slvr! zq6^k#Z0$#x)BgQV0R)mj>wr~D_D`OD?=ItYPr~4RMnC{0_Qbq*5CrZVPk^-V7rKNR zKE-iX6QQ6_gkuYkS}3yfKi=}Wiw?-OXf$XQF#)m~am_RqZSnoIRChR+!Y0&x!DLf3T()2EGw8 z$t3=KC&H^m>U>2Pg;ZO3nc?rfpm>o;!crn|;@{i>er3)Sq4B!0QgBh9Di%OmqL7i?qz3^1B(k0kW0c5qv*XtKjZ1*>~x(I}YCeJW!w1GHj zpCEKucp7Jv^VTvalW4?JkJDS3-`aL#gRm>YIxKPNp+r<1^w_V>H0ur0UpTCGx=Ej) z(S%XGA9N4+X{NpNAP0d@N7G6sa&7#qz4JVr=t|P*{^5oB z=S50oR$~}Z1ThVnQrZA=gf^+L*jam3rtVqOMC{(fW4Rg^83h7sXqg zauXJk^fHWzMx*V;@`cK<^~-%PBNtn)uAT{bEbdxsvu4zsozrO;rZ4Xc>X-q^$~56v zLi#zrcHdu1wQ?vJH%8Ww2Y#+eEyH&E%~0(e?skWS8Pq$Y6Zr6pO?SQAtLxYe4f22C zx7m+wcudNAsYapMbF*(6I>3O5S0M?;CtCmOK8;o$BDniLaN}+F=n{3GQv90w4wbLD zjot^xdq?oyIZ6wj2b|u0GU?^ITe-p*1DYe2oyj;2U+nAH>~ZcKPmzU5{oM=`^RJr% zGNoz*gTzpDvn1}vX?VH~nS#N_jypk%HbK2GYMRLaiDDu}7dScwU0OeahM3Pu$r2i- zVV^os##EO}yyiu!0&Ng^rC|}dbTyB02H?rc6{CW2rWA;^s-uf&ruC069dn!r9Hwh7$&>LrPYkm_*rL=l}{3tJ2395d;QsL<-pluMcAU z;;aZ>dY7chM8(SoV`n=%vN?Y=O@zeAc+Mj*jJg$2wC$;7)^?DC>+q`%U+E{0@Wenk z5#li2L9Z_V)vx10%qUcF#9!g*CTStk{o5k50Tz{wfNH)eK<|xti*9qRR^DX)4If9O zxL6&p?>#z)eP(Fs=MPNb=|l{i)kZGK3@mB}r~Pv6z%gS{C+8>KvPi#EB8{8n0;R%2 zCOtYQGAWRy2*6mn!~*=3-j6GV6SixfFv~?LS$;j84@(|Q6<6NJmloq4*_8?HCPdW5 zV(kU9{nK{gi#TJGWXTCO_E>rj-f9cx#r9Iyar5BdEdSBu6C80i!fJ^f&>Yu^i8|?Q z-&uCYd0(yJ?hrRsQYLuKpQHC~A?U-mKGeoN>Wxp!AF!3!I2 z|JC!Q3IbNB#x3Wv2AYoYvmEWEjmG!w%$@W8FojKJZP@V_lZwkKp#1=REf&(>8k$XV zZLrqSfarvJQ5jI>fJzmw7#JN}kG;pHSWsNE(#v`(Q7%#mcuXRs)}XQ4mSgWUOEum< zu%sTiOT`{^e!Vc0*h$%zgaI|X(zZP~SmIM~!e++AyH_w#;cf?&^V zm_DTjMQi1FS+I7|;E+u4Ae5hcw26HxoM!Q51a3Xpk;=3 ztk;J<#Wriy=&L;bTq99or9kNO=Seu>JC%SFd%W+VC1($(=hq#_$2ykh9Tmm|e^oxg z0^!iV0Z#IIPKA{voP#KiIcOz&MPFi6qG!9Mqz8#PkE&O)<_;4`=(i+5j#B zPXb9%y@Zcs0|}&3ZlAg4<%(WAzQaLJb>^v}W?A@vD)?iy6{x5VAd50?A+@AkP62$C zaE>|cZ9$9qw*7^y#XIj)HdT_h&VqWPhUCLdt*Yf44^`~4)qQL-2VaTLqBER^I=yrv zA-e^1VBQ(m;2t)@HCPzQh=go?+%raw-+pwxT#a0{xyuPPVP=Tldm@}Jnzh?fbk%o> z()}Z85qKis5Z#ADf#^6~_TnNeyx0o)h=D$Fj0NK>J_vTFMxc&ux2>CA)tU`2vapR_m^PrwiL~wGXhYce zq|l*#TE5D${k?{`BL2rgxhzV*T7KAJhFYRjp_5va+;c!YPoiVf&PlrfX*nP&G!LTO zVLM4gN;G>Nvx%=g0!8C<3qXhe>!E)H>CL-6S!$6|scpUp>5ylbc__9E!%`wTjxl+B z3We3?N4l7(4GlUD2!(Xb4rv!zbdpWsFUjaQvn>K5KlyYzpo2S11KQw~)MO;U9nd4Y zNh@&8yo4e=-c?*(UTAc!D2k0WVu?RE;I=)8&6h)fZnL2^4cLQ$xl~R9nrzKv2WCq6bjkhL3C81XP~}Mq3xbJ9G;}mB7Sze6 zCKdMzD{oX588yj67?wj3#s_1TnA^0g6%Wgf<5+l1m85|RT%$xncM4&aTF8QA!Rqh@ zmQ17{2BM7$!`klY`;&UPg$g9`)P2)xj^ph@Zwf8*j%4{J^FrL%Dne_|gUUs>(Ie8N z9UG7(4w&l0NSnSSztw9CB`}WLd2UklSEa>k?_|@u1gd=6=DSrE5;K%08YfjNws-SU z`_P*#-kITS#_2+*RX>mI_8Xq8sy~6vWM}go5Xm7^~CA%((p88`YTLwH?lbI+|eR)cy^|?hUQ;_ek z4?m1g$g?dzK%p9&Nbancn#WY+n30jCQmkGVby|x4TEYAVDuOscq}nZ2M(U1ETCn00 zFc|00)6YbmsIOY8Wsy*vHeX+mtPD1K&qkA;G3YDIZw^DCQagFsboEI%8_DeN0MxKJm186_d4vVbwIW z&dEKY>5sX33InuFuSw%&GQ9ph$LZ) z-MhDmabC>k38Xj9KHND+vqx80wzWrd`HP&&nYLAvFY2?(dhJVFq<~rU+&U@E6;>5b z&z@QKFtZw#hyBGD`_JGS3z!9(@VscEm1k(A$AZaw$w);;iRR^@xzey?kkoQ0NO_!O zQf-p?&$LeFrV|T+bXYQ*G#spAZIbxubaiSIvhh>aUiiUj6k*re*OQ)7lHk?oeS@6^ zOH1X&Jn+T_lib zRbIL&??yZ>;2^3ZuSlgxlYgTU);tkktdk)XhB`)zpqa4HRKvP`BZZ)yaCB^>?Ah1ZwpPH&yq>>OEtkeTnY5;q2iZne-)aGn;SnxIMY1k@MM$&pie|U_i7hslGz;Co&^~oZ)U!?rCcl?> z%a&!gC!mf4HbteAIuA;(jvB|Li#kcwkVF?#OGV`AzDIwiM){l{uextV7fN@WZ^)k8?h+%)4a$_a?e~){E8P; zRi3FRSuOeBtd4&~btj<2@I+;{)0j2x25!_SG* zCAN^3KKrToDg^}I@u7u_^m8sDQ(PQzm8!9T>Jss#B$b+6XO#)pbrIO+c>wnHw22j} zj`083sz5=|D-{Z4s{*x@@!V0#=sc$~ncA3sa(w^1_h@E3W7C#s613Qnc#*wfTs0Fw zI`@+gm5Nw$nQFK8n{FN)=tQ`q$#bHpr6j;AXTk$YOS!njSm7eBeQq?5k>935Xv`?% z;c33n#Y7)_)HIErdeAbpC|Qs=R*XBG^=c7tMujr9a9lt}x5QYg^(KE(Zfx3;XbPS^ zFY+kM=4k2J2By}}WYqHaupSoJ$IKt%N{r3n4OLbdMT1dmdrb1C_2~tJs zrbs4mt8w*~YRr7z0~^IiX?e(_~2BN z(+mmpn`@L)3r!)Gij@kxDR_zz`N>o^n)I?A9E$xZMP^G%W!}ssq*~!lE<*(mL3|=k zcRxKtBqp}Nn;g1y<)V8cZT_aX{x~0@PKELZZzrS+8EM2F&8Cxr3K(L;gV0Fb;yy); zk(kSrp*CxW0i!EoVY~4%RJbjGz`4>*g(@#}OWE&tanboQn+j$!y7@1pnP$IovK7E2 zLC2FT$l`8nSl>h<*q@%eZd!qUN?H;rWH?WbxJeAD_d7@T!sOC*%jo7L>^|7C!A2Wy zi=`vyvGb-XuGn%&Wl8g3$ef+I)c;Xjy;6S&1%;EO{dUR%DTt(Rbnj=nR4*?!W3+Rl zxu@*uUsP_O>PS3G<7*>>6g9XE;`WiL0Am?z2ubkuVvOR&vf8xhmP-{fYIz6=ce=9v zvHV3zYhr1YYsv$W)Pj?vqb{6?4zhI9v`MdWknt;m3yQ}s%=i4?A0`rCMd8K^Ep8Et z^am&NuN*zm!xmVB*Jxx&IQn{ zaxHZ-pj*KW#mNy1cSbPuZ%;GbS9CmxXXySd)cy9LwQ3kaXS{CHVspo#m*+dyvhCY( zcEovtYW-&6*NJeTt^p-zWu zl8UMZS+(}>i9*SQ?|zGU@{3wlRZXPa+)Y1am+r%?-rgR!nO#l+8m;zujplPgTyB@N zKF@b^k9H5a;#VU1g}FDR znRn72PM2s_E!;Z!E<=Ko>2;_{#G{>Usyi;*=i6PZT5^s9-F89B2i@nO-$5&^`puwe(h}V00#(O`3hpynw4E zPD)f_h^}R0H$2<#{pkb7@T=K0E;hS$>SPW-|0M=dQ^!SfhIMV;f5ru(^^ zM1LoNat#XC{+j@?Wjz1;Z(&`(Hy6(9UT~A9CDjP@kWxqQjaG*`BVV%t1PFp_vAoRVq~b25diS zdfww(wmhYe^%MxzUaoq2NW%K|&V`A~8r zQf;~&Gxrz5U*TT}ni73IIKU~T;ZSp3v!K}FETjg~THt**CoUPmasuulMSAbT-+cib zN0aa4)#(wvegsRNyb}$H_b!$jA?$p+$FDwLC7S=^_14xf5bY?-O?Mm(O$Dd^Uq`=a zc)?!j_U)Jx3R%piKRa{0lZOOJTHZ0l!o?)vBZ1Xjq8Oh}-=k5bFd$c#jml#FH;dm<~FQki zkC{=+Ys{C|IOEewR7I!!0RVqRI2D(2K~ z@xI+H*uvjmZfZ4eI7*AN_aVji4T7uolEm;ZPcsi9;Txl)PW;yKxnII7R-fs9x#SL+ z0WM;E@r~A=JmC35)_dl)5CXt^5Nc@Rr*&f4jeVVE+ z*~&8Hw;UaH9(6#f)f~^NT41>_nC*WGtOCW2Quh#WBRp;NmTG{z}Ga)A!>1RGpy?+uK^prvOcFl zl^Tr!AaD_2d?zKx=7-OPFxdXKCsX?WPiptvKn$2^nE(_YD6}6&V=W&BJ9Z7rC zCG!qtCKW3RSoNgS?R@|@7Y;f`yS)+U4Mp4eo)Jg5HdEf*4;30shVp=r&4d*@1sj#U%etES45b$Zjg*v zIq}zbSek`CD1u3%mg%_FM&s}ops=OB>b#9^2)_jKyjm_lM#Lw7gQljF*l0M?fyFkq zy7s5x^HGl9_%!zwtV3p65==Lallr1r?iBM&R$1~^<9Y@!NKr%&yGJ4kU=R6kLFY=;N zF$~`V?eTqy)w|HdNuA0$Nhell*|JHjl3y}QS*$|+UI<*u>2-kid-G!3JSjppHZjQs z9*eDpAo4mj4%H@BQ_IGMSGLDlKMH9vc2L~J>yrcZmQj`yn*pL~`Sru7~nn z%b)|WqU>J$-;gBgPSX4 zJ*d@8A|9fkw^mSf)H|1AdI8}0aG)5UwUIgT8)fFiX^?$-qZrbb zB2uF7%@<1K-5$j`Y3(TcBy5(kZcv^0DYGf#TMZ4DdgD=ar9-SpZIfb=MmCn*#OOB(PA_hG}mX;!3g z?Ym(HwLZ%d%zU+j%=D-*S8y{44jr4W>uto^PzJ;#arRgGy*|x$+gs>6KDkH`{l6gI zmLBNSyn~E8MgSflx@Us$Vt^d9a21hQPilyypfYhAWqU9qH}84{ze5e56c67D;5R z6o)6SRE36!?!|2_X7U0P^j9E+!0w~oNjhD(?VvmCIERJcvxhO(Cyt$$=X)BwD9~P@ zm!g-#6R~=^XmLAwg<`<=cjyhVs<(}L7alz(h^J9F51b_PJOGtfZ0{jC! zio|n^cqG7Zxol4S`6p8_i103t#kCKjorBRnU%-#x2?0PIkG});vg&6GUFU2Pj2dIB zmUu|_U)8=Fk4z)DXqR&5+_nYCl(edmC=e0YXpoT%%b=eA`%Xxa^3REptQ2Pnb`-10 zOy3}*9s;XVTD_V515{Wv24x8nO|o2;9Y9Uubp{BIu$}10h1?keZ9BUow~K}0eP&C5 z!e)a``_aa+23dFOqZW?*iVy31U?XGO?YuNYn|YJEvquflyYCIjG4**(f_cXD+iW%x zS0Fcne<-qVL8`hW#&>cFc%qorQ)aP*IG<%{QQ1GeUsSmNZ{|e_-rB=GFX?<99cP3 zaZI9?WVEqGK}F1duqU+6vbmpPEb`(cw3?}6QKwkuD&3T+Y>KSloiZexT(E8CAEnr+ z*%uN+WLZP<4grhqMV2Aj$xpTFFgwg}pg!02kz|hMqG-eIsKT_Iv0tDOFo?Q^cNGw^ z_tgnkwCs5OLWF9OD(yt%VKpfn3`k=?vEU2d1VrGIBAyXouq7IOHr0l#%Y;nQ7)Ae% zkifQoSePIeu7eqr*sXZ9YL?@|fNMO5hEN@W{z)`ev|}qM_71+4PN-L%6mC_N(6tqD z*D0%G_41)fW|BX%FS#i`MxAwS@egpT);fMKz|19uF_AWQxN z)+>PMKr}R-Bolv6rQzYX(Gls`qaN0|;dRwz+ey(NrZ_EM`#q=5j->P6wzIXukf~aZ zN36+(tVnj4z$ok+Q>5fDABM$p%1vdR{muKYcS2;bQLeAT<3vZgolVPrNVY^LZ_oD$ z_`IGFNE93LN4^33ykAX!+vvYB87*s}1Y#ng36uMHKh;(+^w_AwJx%Sn1&`s71Pij- zZ2x(_gAg5J*6dfK;H*v>@}ZrSPEgMYrmlM)G8tZ92TV*ZI#o6ZGp85O@a^bHI*!2t zdu_A#n>Fde)cuXS4K#&V^I2v2>mbX8z=^)&10z|ECV`f>3k5%5iL6gmHLH__QCH5M z49N)668MwI`1xryq>m(4=TXC6tG+AHMWMND8Z^w)QUS=yZp)_%8!bd&+xIO{y`n5N zV<}{xaS5L)@M`8HXvXwR0qaZOSffuZUeu|^DzGS+$R+%!BLL<44n@glDZuY2G+iw+ z0B?{wq&}?was|gjDYyte9=RF6&~WKy(&o)G=9W*jrCg1-H~Vk1;slbJ+!7U$kdkrbqF0Vsxvy3N?GG9ZRTGI zFa{OqHb!N)m#N*p`DO<*byd~!eU3Xmqm+9gk`R@Rm1@EPdmt%4M9w&}8okN!yda_&zI>FoidFlGb8Q6_Tw)L!TWr;YkFEEqW% zY$gaMv_Bms{%eHxPX{R-Jrs)u@!MyUO%^2D7M3=o#YOB~c;UOxmqM_RVdEPKn zJzqvd^q z69@fBDTvu%h&S)T9#Om@8n+HpA9D)k5%@ZAMnVmkf(qr_Da4leUq9-&oftBZZ(ciI=^F2pQPi1hiu3z-8bh^u+PL6; z@x%3$MgTac)kn#xP9>Sibx~;9!0mbvMSOH7OptRAEPk^MCNV55MlW&dGKOTAUmQMm zxd!Ex=cU75UpDU5s0*dpu&1hAS)Hc(q=AUXlMe(%EIh1|qp92gl)P{_gGB^cy_C8< ze=hxDbXRfHGu*{eF0!3t4f*epn&VbP`Rh?(i+J-}Ui$UxaS+Z4M6kx^+#t(05j=V3 zAtA}dPEJQmJWxTrj9+*XCOugX=cymTcVyo0aun1gr`Ed0DNZQ1~{2k(!FM2i|b zJjxc_@57-ib22Dzb9EK}r*$D#hH&W)TSWqsT`G$-#-d@Jh3M@<3M0&@qf0nJKcB~~43vo8MCHDJd!`M;kO-$h0t70t zTY8t{F2eVo{-7b?=to6uVoCmjql=@ss{Uj|WG3gCEWymPK(kP&{DoU6L1{l#pUCX+ z28Pzj{)W%vp~r+h^_enkT>!sc=K18A1=Etki(9YW$FnhKm4;F<=MXC*nflK3>Jcj} zgP_hLNDB|tfjifhO6xRIglbRlWkXe=I&OZxWK`qBPXeih^n55{)znu$_qfl1`|WCT z5j)zVM^%Wy;*Bx=4%nE({YR$utrd%bObph2)8Ya~bahpfwdnQtl2#DjQRd?A;+>z+ zVCgN#D{&&G6^$XB??GP19rCQ44zKPht$D{YCjgW>eP;poK)!vj$_5#d|1WWLl=&i!Rlo49b8 zqkuXl6cBiQ7}I6I#*4tfokf-KKZ5#Ru%Ln9bDj*o$G9)~@#zuggUolKWQQ)($&H@n6yI<+2OQb~gLFRoy zc=ep70|U>RhA?VXg=B5xqlZ;`pL2cq8^tvMN*tKXI}eVkG)2gei4IH3)bV!1?vy9r z;&C{=5oRi5=2PUJ8k`nd>|bt)O_B9%pJnyB8tYm)4p%^9Z|t!?{kpU{STS_VLoDeV zID@Y22+EvhawHln7$g}RQ0p&J)PLjnqZE+L{=3avO5VMJa{E2xu&^hddFxgQ6D$%2v=;ZH#8*CJLqrDC|!h_9d2uSruCjUx7$ zemO|7%z{mE;-LQVq;_!pD+9r$GDI1sT@TKy%FK3vs z{t(qr5~4Yh4+UC0>ILoAn759Z25IZ+1|!~ zl|@S_11fv^IeItnZ|5Bx1~pf%_l5(*Cu8#Z9$hfseSB?C6p#=(cTkl}?~8gOOf^a5 zxF16Fkql*!8z57^CB6Wam5doJXMhYkl#W(!i(-`A=9`)iwjPRlr|E0YaI!A8UeZ7+f}+vPo*)tdHFY|4tj*T;P_)oN zJLP19I!1&YK*i$Rxi>U|0;xka+;36OswD%ryd7t`&|hhI=et|yKh11VM`ux z@)Q^9m+yUNIu9MJQjz!7+X;z*91sks#v7v0)n91hG&>5==W4)}_D8(ZaB6tkp@hj%rx2Q-?Uk{iJ^8-+aD4Jc;jx6yy?aIkk zH^WIGJP0JScGl%gV@t|Wo|ozV)kW{;{))esbz)M}H;RQK($^p~LQ_SlErNh9HO};F zog0k?e>T#FXz3ALi^?8g{`wY#!S&dJXMtIiTe~UY|7Y7N6NzKD=!bP{-3eg-HivSJjtBFiU$|1>tRQNw7dNpBC0f2_8?fpcX*_1KoVSp-jC? zOP?1}w0AG1PBD54YNJCke%y{rl}DS$TTIZ&X+kfh8&IX24AN?yz^~tvtp&ewDz4cd z&P4nZaMU4AN=5n*+drOf^6lo8z-kwLSMOS|r*+yvO2?Qt`vnm$dkYvJFb4#X)^7yYAoZ`xo>SdmY&cs)r;XgM}3Sc3kYcdsJ(?pCh>iB)c z`AFm;7{molkW)%fu=>$=>pnl8D|gXv_{C7XPt&K;2hi)&&*_pfA1k;4R@~FbzT@s^L9{E>m~=XzU^=l6b~a3GAAu0F zo>^R`8eG@NpIT+p=18_*Mn_r-%M%Y#i&Phouj!78a*Y%L7Xo~gslgw9TeBMGyp5LqXcZlebe2+?9gvCOxC1xWW96M-30E(nU@ z8Jyj$QbYI0ISz0Ysct<`$PEYCQZN5d;t1<_zj6@l>~OjGaW(R2T1&HVNASLOedHVscrG#h zm#+99Op*itm6~PgixV!o2yNHXO2j&@(QI%|;5w)H4u=}P?>ur+f4$zER4H5Tacb8G zz=RzXpN7!^ucMWl806-2tL~4MO-`aAmxZc=a@0S{I9~hA*6vAKpIdgRRPDieo+zB<@|^rl zw0DnuCVJs`{g*>_*gIO!Am!xEP3n-8Z`l$5)6IOvM4gfgi{O|4=I~zqWg!gV+!blJ zk`PJlJ<{?;MehHYPx2-FX2w2_Z@9dUm$J0+j+Q^Osp?of;sL8vZZqX5NP(pu@mfpx zRACGGa|n|(lwmP>28*FG0T80PN31_9FIK)r`<7jJ*Wdq-5z1y)6a@CD>LC{PEG0(| zAznBIUN5*RH{!V;GIf((Z&CQ5u%DmwTZM6`T4ft^0Ei!~Z}GV8@lf6evhD>}sLyf6#{->{n%DkF8KvBPdcwqe~@9 zOMoM6xcMfu$n(uz!+`Q#5HhQS;@9vWn6y7~a5yn93>8nJaGcO(P)<(dSxej;)~KAw z?VPBqc{%>yUM_*p;sIj*LuCFwvx9Og()0J4X$~~D^r6_hxLqfeZ9sXA;k%0s(OBYj zr!f}RFT+J?M#sa zvojbI)h3lrT+5Sq_hU>l8o-LKS)u%IAOH0h!NW{WDDJbW@WP&(-EWm7yC4@!Q^dr+ z%a-R?yZ&mCnfk{6nGF9z^EX916 zM5W-2=e?a9>L&2^)VG`?=Hvhd){s;OJqZ029H>L8#SVaA| zFtEcAB0*Axp@y~_F8wc^*nfR^piB^WSea{b5YfMV0~3T#IAGHrA|%hY%Kz{{F)-j^ zV|K-LdH*el-56i#VWI&%IM;55|LK7c{~2~9xZ3W2zQVtJcmgP<)xe%V-|2VHTL04n zVf`~~BE0F^e=s!4%g10h`=d6z;`$%py|2{&3`>pP+xoxds{aN2ib;Zfi>mYEn@;|> z@bJGLZ(8o3VdY3eT>Jh*{K6!_k^lc+=>OLX1;IT~k|=3C7zZe49xA?_{C6TV$=XwFimiAv{qG*}JQoxLW}kc#?FcMB z5TniI;e2fmj1Huoo}T`x(R3#T_XeN9qzE-(Oe-T@woBq^{hW&9f-2t@j9#mY^(sfF zLkAj(fE*ZSTkEQ5TvqV9JJu=!8lp!N@v8W}UdcY4RCVow>4MZ6^P$#a0oUW*$|i8% zawNz5oB`Y#tvaCrT62Vu;+b+iYk#aZXaUI(5x=d2H1+ z66dc{IqPDi(%4t-yn;8|;cTPl-@vLh`l^F|>bDro3AX?ETY7Qqbny3u`*`KiZeGfB zXKZFxdT8JX-frhv;55nDayOk})_p?D5^V1hb@FjGEdi z^Dsnhy;NI@BJlmCnddBuLTv*fI^&Z;j>n5PfuLtBQI&9OeL@dDF}ZbwCFqXCf6+p*#}_$>N3?#wgz zAkSoBFpV-t8BVYM4{|01W?q6)ss%>I#*(1X_{1jpc2GtpCMqg_XDbGEUsH!I0i9#O z5hXewrSFLozSm7O()52^_r6F0(@m`s<&~{v?a#;M3p&+xVPcsGN?AP0@2@wie=U-f z3oXY|UG2`wt2z_G_XYW4)!dwF)WMs*G-D%{{I{uXQPhDYfgLdZZ)l5o0TlTbr*}}kg`ry>DOPPA7HL*%KtKHmG72{ zSN*aVHRXBozZ_|NL==Xuo}Ko(*LE(~ZKnG;Ef=Az7eHb;bB-|iO460Sr_i2HJI00H zC5zXCXV9NZ%`8))p%fz(_Pn!E4qFrqj!G>`IY#V1l+ym*q&6d_xIEvDmUNBY#re}) z?tL5&MK5ikXD6uCX*VTMs#%%+DOM)(JZo42Q>wHK8_WNYB+O1dNMICh15IHbyxy6> z?Cb3hjdcm_s@7vh0#QGoQCIrr`(+#CQS3#jq-Bcgf^dqCvWjhmYyz$?!|yK_Y)Sy~ z|MD5A`H9K*u972-Xi4zH*qRis`ZmeX}|$e=`&1v+*m} zm&)~X4*`15lGaY$pDcRw2KPHu*)F=BrvMA3t};s%Hvf#&WxGxoKXNvFA+1+j8%(nQ zmEXh!%>lD>=jEO{RK=&{R9p8gWHsD02BRV#s{N$>ri83MCM#Tz0$=%x(jeC(4hJu7 zR8=)Q%`B#D;BjL8;#IcjH#Mcr-68n_X-yKSbfp-!rMEDBlwDCIvophYoQ?1Q1;$P{z<-pg^Wsd)vCNE;KHw_3^w(==AYUGT)OOmTgP zWU7pJU-zWDcZmiJ=2d6!g?Cf@Af^QKo~qdq6kT!65tEtRV=b$a>;xa;PlvzacEQ}1 z(+`K3E^y5@sKopQ3=h@4_zpOAf|t->Y)Kn7b(r9Tq2xKA_eO}a_MrweFwM?qo9fAP zTvLZul-D4q<(^)z!lo_6HT6Lp6fk6T^RsOnwd9;8zE;X<@vLh2GT{S+VfHx6GNfqV zpa}yD_43Z^j|5Xotw9U6wYgqxkRAJ0&Vypt)4Aa4Z%Glh={#IXG(t0Uu0f?KgJKi% zH58{AY;$$}3Qt8u!T|&;<{CwxL`F0pUR}bsFkG{`K8o0A3=Cyzl&WSs&)1omb_4%N z&&rUqm?2o3lPI1nF0jE_32%YPmZQbBYudf8{$69ilH*}n6s z*Z>(IsMa@mRFcaIJ}nGEnoV|VFgpFNOUgoH$a>9SVryqn^!{W?^&hBOm#FbG7Zg(v zpz+(nC_`g(N8O}=qON^EXyAb>6DH}`!JhwOtq;oev81S>W}xt~!+UPdA~8`@F_n?B?Ki zIsD+!?T8X`+}lFs{2U{=un^aN@qBf(Iaj0{)xXXOZ*&{-TA|&BYs#S{g(B?6=6hQQ zEIBtuIB$l2SnJJwp0fQ=?4RC=Wx8-)(t2OJ(q4ylUl(H)6xE13=Ex8 z{y1+;5N+sfeCT>DLh-yK;&m3;9&t`Gaxcd|Pc1s`T$t{6IPWd0owG)7_B(I&Bx^YX z`6k}C)Z{+0VfMxO1E>yG?^?gtnX0yYyh4-IKeRu#2t7Z$J6*pUj99~eCxLIG3)7wU zpEu!z`N}vv=D2uO&CRDf6K;wT6ShL;&|}VNcf2Yiiz51X&&Ch7GMl9h^N`mPx4Rf)lJlzF)2gQhQ3dA4| zx4{V69-+&{3DOwP;2LU4tC2~(6F1f2@<;!!XFgOHI*f@E+$vQleHB7H7iZD?COO#o zdaa00kU(G8;r-w?BkTAAOU1VJyHGcffJ)Z^d_zz+W-h8g7m=)A{)eND5k7}zMo8Dp z4v32r8EyT1p7l}{0oXPhcZQ|H@u@`Z^ge6QNjd1dLp~y^O@*N01 zq#PFW^Ej7)b31P2dTCdW(J+DJapT(h@|jSx(DJ9?!7YDOVT%!yPls+Owcz@5|IsmX*d8H$ z!G)vyG4+?w?``UAEf?+3W%c20e-EhM6E^53aWo|AXYUeizDnuJjS>>#@?oe;P z4*s@GemN{Sw=6G@x?s(^bHr3nTA+$N!5587>Y7&uShgCL7qfnyk|B(7o@>xn)}DZ6 zHba5=l_>JQ@6M@QwN|cZ!2kW>m}8dXNiC;f7gQm0jWRkcTEe&Y;67lIgLS;_ z#ddP&@yC0`b@c`Osb-Y*asr4Ci?C5h>XT0Y7)4Lx{qJLG> za{Kn_dcuBZ58c*0sFL@5wKMcquOaw*`J3`S8mduW+8Q0}VHC3B>f_m5MKGMP{sip3 zyo)bTZ0O`J_HTI=^0Op)MpGw%#`uFr9D#B{8TtjZ(H3QLwAb+fRSC9@ODwI!8_hTt zn0z(@j=k7kRdvql3nS@r5#kJ<=tTSb+7BMSU42K?=N6+{xIYB?ZW2CJmPHBtxsKaF zTc1&Wc1dALuJ<%GGcD`1V)MIA5((b;={~S_FbMCls(YgO&?Xt5QRPo^k=cIJvUP4~ z>APh;R&eSBCDf?jRni4o;iR~~dN5yod#jkDpc9;1+t6PV991ENVPXm}tu`y`+Sqs% z5Emi1YC0|J;>tRDwmZK|BKt_Smr;)AM^62wxxwn>^D$xmFnGZup4&O=CG}3De^X3I z;IVPGWP{T)9t8MQ{3d`YnanB&Kw^O!q4bre6wjBwl2vP0+a(7VaVf&Um)N+^8omZ# z>iYLTTA+8E*)#Bdl|6fojPWzDA*d87l5RJhTUgG|uhZvo>xYW^p7%zl?;1M$iwSJt zom8f|c;G6g3neJIVAoOf)m$_o4`>YDc^qM40x)5~aMRBS5d=M?%3rs(rt-nNcCAOn z?`q$pu>8RCf&mS;eii1Gz-|mvJST!irA;*Vy%ZhV1|Bg+4>+TsevDcb7$5?k)71ce z8+~=>cjXmwj*x&S4k-Q~;}53_xXa}k%1-&4b-dC|&`JN&IRC24a^#1f5(I{@iwn%+W z0(DOF-7;vC3y(a;bSs0{`?SU`+sPv+xHevH9d%emH$Bh9smI_gL&$om(g{`T@(nC@ zs>`-DQ<7(v17G4YFRxLVcE@!@%XLH3(Kn14!T z=g>MuQG!$2XrAFou^kKfXjWR9y})8ox0rgfBrb`ZGG(C9dj1iH1N5!aFpXq3<$l+a zYt(jJ1GeYC>}54M#=+Swsb={n*itOXRXs-=>DU!-F8-J4v$f6-@`_$d^iHa}eYUF~ zW(GLT*!O>dDx}p~KP8kPSNBiMqK z=`OqjDf?I!73Q^XTQz(j`bGsd@ui+RZr#+}`}-g>nmy&ezrN={5VMW(%*#fo2;?jUq5t=X5QuwW+<>e=2 z4Pxlhj}KZM6i+rF^SQSJC)_p?V#mqTmZNxs5A33cc4ufwo|0v`*cPPrBdakHxz?1^~v#FrKoLH z2n^rC#>J3}9^?h)#h&fQiVl@j*D2*QTvgLmrs3Yz7`pS2YL{iY()37AHOBZUt8dN- ztzqw;PUD3uWmr5ekTK#hJWn_NnBx0dV6@4?B@EUWVWg7-9Qclku^q6zy#(M?j7?Oa z?#zT?!i&tppS7HhTSj1DqE-0`D3=Hk4Tk`Gg2v0Jj;7!J*u}i#q^CF2y${+(j1`5` z>p_w>5(Xi^ch=;Id9?T9QHOQ18sONE7J$qqZi0eP1101idVR&UeKb`N_VEn9Q%Ds^ z>wVlU8T+c2r2nYM35G*;YGL%Hz?p7X)K{cT2sfFz^?1AyH$$ko!c(0zw?x#p^3Ap| zh$GU*J4bE@1aCInEgFo06(ZTy-=?3H-X+e{{Aj4cR*`KmJ<+>=$i+w7Eb=&=f(2uE zlE{5xA-S4Z$ex;9wcY}f*$Kg>P$U~CtzFxMQR5N~_Xqwg68F{E9BvwCuyQ(hDswgyUa zlxw8>&zBhbB$0O2%^)o%x#E*7z#^_FE#Q#XcekZ_A03&W6w72t_lpvi5b_w?2~-bM z9MW|glw{_?lOV<5=Kj*ams3+x0`9Ci2`QxMsrR3GxZYZJoCRA{qzXzkQJ zhtX_)<7-UitOg5J*E^Rosa8>NhW{|MRB#?D0$!fIUlw_8jM`XK<%+o4wsSH}s3q93 zxQX<~jzPtSq?fgU5s!8`V3ex}6$^P{_Ys5~}&)#O${=sHyCMT-eaYXZ^9U<83>*#M<8VfLU6;!%F zMMhg(om=pAwbdH0Df_s7&}@-Su^A*ox{g<5q@%F*Cu zBt)|E$$gJJ6rPnRPDvqP?)h6$-*N=(t7W!aCZBmH6Vdy`W*%LblweMgIi@0KYDMR_ z4%V57V%{!d8sm4N^@W3nARdg_D4{BC6H2-ld0Hnm&ijsAZAG3Vc?{9gqO#iWU02^~ zf4!SyAzi;;o@_MJ_eOgT>hT#O1{OLLSplmnpDz8fesj0|(%-3FKoXM7ne#J=02}AN zS+>P^wY@8)^BemCghx!fx#Rv&&{TUm_&0o?Bn-^X6^D^n{`e32seyIqF3 zpKUb_k$-*bK+K6fW9o6*o7dl6&-xQ#DcI?6LY(UIjBm&EXh;VO%=NIl&3zDg^zqfB z5ml$*s|4x$?hID3{R$b*J}{gfLGb@$yP$D6$$%Kam=FFf0Q?5A!47DjhXQQ28j<6W z6j7jMc@j*GlS07u%T^k<rI2c*2i(0V@)fsD8a|%cdzlY^VNOuuP!stj_YPDiIxl4 z`4%~!`w45GpRvil^fB000j}*pmBn|zHLM0#rDqi3yGWQMpYo<3NK`vEvX{8!p2vf& zn85XrQY;&X1%e0Nz+sW5qN1b+mlah-vXU2c?9oc54Vdx zI=$mMt||mOM`oJZ4)(!0O2+-oVR??DWiQ~F^RDI}ANX^^Ogh%ZpRIK=?)*D}x*vW| z-ukCMH`L1u1-l;j(O)EM&PsW^b|=rczskADt1r})rIs9OLtnUAuD|+dg4WmAP~3-j z1dbc|Kt6$iOTrhE7hW=ZH)_|XssoJ*SU`dtMTgUVB3$iiGXaZUD2rScQNm~Miyzi(2Hk@GW%l_kQb16`g_Q$`zcPwv#C#lAZC?Vgjj27 zPq(Z5Cujij1C7{e1RK%D@y+i!OYsA}^D%;cih=pK1LI}F6eW~qVZ;6oD}2irY*H$< zuuZm<>_yXj(<$tC5nON-U$6@AyZ1moW3OKQI@&t*-7h9F9H1e;gg3x|Azoy#gewiU z(0KXdX(+l=`PslMT%vZ2wXNAdUaCq6wu#=O0kc7Wu%XX)Ub{Khgp6F#C`ch${|6T1 z-8Y)(axzUn;ru*b!-sUW1Wo)7U0;OOGg(JOpd_yD(c&N13io@SfbgfuV7I12p2&?t$c&k>R0;m zCFS61S`xj*nSSh_0*+2P;9KEr`ZaLv&kdiX58%N_h+}ts;rSe0=dTlbvZ)z)!tcG6 zY#pY8tlw9bvuC;XbTd4Ur{sR^2N4#Rn?05GtKPMo^TG3Bj++Dy(f(jF2AhKYH4fEv z`k|2EG?xfdfyYu@-o*#@zjtqn^_EWE8#mXKVT2A$ZZY7N6zy@CV6okMjRsMk>TS!9&VRzK6^rYWGtMM~N#9Kc^RDvj!B3 zRv=j912`EED*$qT^PNB14+Lv2JSq%8@H7xq#bKAF(7_9mzli+)u4 z%S}E{hX!OoHb&_7|1$R!@!iUSi1o0u^S;z$fyW3(_9~t*%Z|HHcw0Lw(+C*sz*ltm z#_ta=7JqXIpQoIZwxNfSa^iks&Od+xxb_*K`vl56intl~^USebwA409cUJ2~Z}SsN zXEURST4v=)eyk&#=;jXQ)Z9Q zk@0DI?|jQYpv?Y~-7E(>Bx|pc=*H+UnW%VNV9s_wVdLD6O8?6O)G#c-fE*{c3w8OBH46 z6H{L8xP9bf#}RnuJUAc@KordB$%$wdlUZTt+vM@H^NVV;(p33ldUpA91Og-?za5Oc zi~%1eKV-3%3WLJG*d&F-prT0<05yhx)-7?MNXU-kzIM2Lqo+*G%FcKY3SD4h6K=-^56bO(qIU*z^0SgUh26hgA4P?9ajvr*O3mnC|V=iFrZM(#)S;qhQfC} z)P3`*2^`=^TR*pF^)f`TyCv2w*w)DCW;b%@00(%OoaudNlklxDaI!KjMc>y)EZ)UW ze!0Rmm)k<4egy*Zp#K|DZcQAL7n8Ce`n1>f!#Gnxd1n#gNi~~wcRZ)tRBY!X&j*Gf z{=+Ssq zE?eEFwSM^NFK_(bhr2(`#|isjLNMy2v^V2KUuYR+Z^j8(IO=EaeVE=uP*Tr9+b3}4 z1|pE%1=tj@qYPiVt5bBf=lbFEc1k+qKZ9xm@|BzO1dUq_wFLBfg&DNVkO9|1c?J=- z<1132ntN7xkfq9B(1tx3!0*}esL)bH`r5o{2mZu!w$na9!$$U15WDJRaJH~}fdj#T@X$%o>?6&S7mDiM%1jz5|9AW(pAaH+alvYLwbBOBu z58Wcylf~z@bpL)A@iG%LYUF#tF|2J21bwCOr&<>TsCkIhR^$%xJWPB!6}~FO+skZ0 z_eFph;8PT?S*l4{^bkNIb{3>p$GOn6C6k0iGIiA-n!v@v z>i3iI0M$QP*q^w zPZXRKP~P9gyOi&}qDIWNcKsoLTdns|OPX<04M#UBPO4!gw1YKeTO+a2|NgCD0G0Qf zDtl~O#xUq#cPTb^t{ET`nOl#y@eIB!No*5E5ClEa#&{-fvwy-;JUrM5j=8;bwvR=i zo$eu(@R@O1f$NRdrTc*sB0xTd5k*pdPCZhD;3$_FZUCZ!!{INNM2;xZ$I(f{G5>nk zo#?s15_df1O(QdA<$jtY5d?*a#^Hs)q5eUE!6Y57QzX~~s7yB7TPKd=h}NM#PmJ|o z+l7h14bj4tkKz!rxukx`>o3BL!>4PVfyRG-d5h~JcFY_G5vfLm^N}O8N^gtnBs>DI zUG+f~13QLpubm1wE~mKe0tnGQ1(|8xF02V~DQ+Bgif8?-Q@?!wQB z{mU#{`_aewHTBjdH68{z4@Z4%vID1?Ji0cxyhoXpGj9HS&=E^EYddgjE?<7^S9bJ2 zX6Ryh`rWYhMgpTR!Ht)-xXKONxVD!s?Zq27oh-B!UeVKh{nv$&&1ScnF;9E%TGTxq z&D#QwT3S%Wf~+TBm7`K^RnnR@BX(Bv)k%0yPo>S5e+M+pDw(Nv;kl{h8m6!5b#368 z4|9Bm%g(!CJ?Xq;zEYy`?PH)bf~Y*%Grx<6>}Acq&%Ql*ZK}`#01DWN4r?bN&=uS= zCJssx?YkYUpbN|b^K&mH*e4t$_(;AD5*ZHrw+Uks;yF|u%y9ai(*_uz+ieCBSA`v z(VXG9>(h{8;hQVtLj@u{WLcqc;?2s68JTO$-0KO;D}JmOK}udBsX+wgw8*rb7e0R{ zN|INKCQl6@=_Rcx)JxzTD}xFHuc%!GAJufwmVs$+M-W29gCJUJITGMGFaClW8=?MJ zfrOEMP8T1tZN3*16bcJI@j>%BDA@V;Y#kai$hS&S0C`rGebW>J(mEO-_}zUwuYeXx zQq*u@Numm!1Er8Mo@QR};>9j6nAMoJY63y7P8d`jrVx?H-yOUJS) zrBDRm>Q}mDGMzn_0zpk?Zrv_i7|g#vE9g=eIO&m5xM9dfRIXGQ-yq~*`_6%l4X;E_ zJ{OjagNdlc%f9(39r6hGWx=daARvEZ zkf)8uDUCQyu>fFykiGM#oH=2LAV$0hsz2p`{X z1a>K0wKk#?imp1^;t~*|?N?guPhX~$eVBgRDjGm}nO1(n#IG23>S<)|X15FFR`B}z z<{PF_iHnh`T1`!`;3pkOp_HUMiJ_|mN%90^lM(3Wm^LoY(gUHpN-;)!9*pd!jnBRd zthP`9rJ#K^)d}Ip-d792I}-GfhYqXWFw2nQTeph8jZ-z4LPFsu#Xz`(qaG}>TWkIH zbg$>f@OkEbU~1>%{=$%uy-)~GklB6D^mQN;U2VH1tcFYZpKC|zdwMtJ5Y>{zGFqn~ z!jU3%CGQXv5Vt$td%yx-TTe)+pN8MwukVvEoh|Q}5QGjeGbxhVj){#OX1ZHMPDFrO z#Nel^35U*g!5vPciUvQqt|sAvv5xozVXvH=01BLqeBA^$3BXBM*CF=AxdciS&X^sd z7v?Z3koo#3L8q!6ifiXhwX@Q5tU5W9xK`GDf@tKzeb0}C9!W`{H;_1M z`}^<3{pO+`w4uakX@dQJhTvEoroDNx@+tZYfLvCi^$XRhHvLh8>3o7 zwHWM$s6tg|G`y6X_^F!Ayw!SUPtn2E&NP+*{RXLjbMEvZUJ46Mf*@to)1@c$(NAbn zKf{#Q=>?M!I0bQfC7uV>jcZT)&Pp=^aCUJE_R}>iVNCBFI^=Y#5Tqa5?VDm$_r@8mN%aoiHq~GzSxWKj3 z>s-+QPno+c>7L2ZyEuYVvyeanemG@0*d8&iTmy1T$25z@4KbR%8nj34G3M_0kd1&Z zNq%q7{1hiMxe($MzSFLCgL*9zW06pjigpv8)x+CZKcqRMOh~R+zFI#Fz~j#wWOrj! zOSFwIk3BiQ^>OpDf-i)Hd%M$;9%0+v%torO?i}6mQah{IInZMt2pF(9PDY{=;n^hx zXLVkQsqY4yqODU!e8m9yV#j#MckQKni@$%~(3(k&T2xO~6nD zGI=S6k768NWp|g(`kU0M&qZ8-ARaL@|MY_WQfZgI@Nm?R71K<1_QB;)2mh8S+R%V} znv{~)1-d3jmc|vS&zhI*0~K+}R~2S`18=t8j==`NL|DKq5407?o+62Wy3C4_3JpaT z!SchTX4SL-7G~?49!Sn7v$&XjEVgCWh|#XluTq$T-f6c=@FP7n(;_=iRD)hMXM^;E zfZr*%!cf(dtuEaD&Q}9yDsL4}yMM25zqYU%(pluoYgrBrcxOHmtj_GBHA1qf>MYnN z>gS&CV*4@^4J}#sZl_<8O62W1R6nM;YGl|wodc;6y-Hz(WF6$HF86a@;#iebU1aPb z%Lx)t5L%X|03Al{O52o+ktJ0zD1bfXs6)^l-y_T#fa0Z;jymL&ruCK1^YE2V9B4vN zU8EN6)0bQ;?41`|kZ1(j@qh-QoDv zFZ+B|wA?N9!Q2>4*&ntogVKE)>SNoiokUA8=UPZScco#|HJ3I9DAFOuzy!5ffdeFg@%XiPP87!RW1yY<Y zMY91E%6C@J^1~uu#?v|T@;C!14z3X?A5p>em5eWjc}hTl7-o}nmQr{Ic(&5)OQKwLgf~aVY=QkqUY`L!;gzbA4316!;Nu|;+0L6x|fMk z*LG!SzU7|UCg5_aP)Ym}2n@;NeG9i-=t|f!=q11kgZIQDi z@Qe&WX1su38h58*?bSZ6R=_HcdPF>bnh3$M6;thJ$Smi6!I*?(&*HVudEfWE8PiO8 z54RaO35cLT04n*Q2Yu?LXUeia3YO?J?l&3gQhka|^C^jSCrTvz$Y?N!2yV{>yV`2& z=yr-L0WDI@0x1w1s#?w*m8mEA!|N#4X<7B)@ObJb$lc_oq+6R>A&}v-xit(AM{PK6 z5j9opm&QpNHYkIL1<@6|v!XR+_PW2!B8Hjay019To4 zDM~&s1_vo7khLK!%$Suv!-@xFm;+@pKafJ;wb5itrJ%p_gyIrl4R<43yt06`;Ch-t(4_RT?F+GF=1p8{ljN&q; z(RBb!*Y;*jQ;`EDtlxJU?)Uqho@aD$+>}WLrStDNFP@54u3+lvRn16z;PeOoH=wR; zinwRLy2ehfa8sxJbb2&#@>}z>iV?3Efnm%tk7R@Uu$+gX@un8Gvv-!Ln|DVu0|q{} zPS~_p^pDw>B)-uEZTtjhuxf;7c;TAer9T-%&T2+~5aswHfw$m?iEY|((OD1pru$cw zmx!eR{dQ#*o%ZEKoX&|?n)SmW295 z5TZ$UzCCn zYA>?n@<;Vz9tl!Mja6bG)z6E8e$N1VGO-pyB2IT5Z`jncL<(^eA2%g+=xu9&DvOaC zM=SFwqb*^dX9(Jm;uIp$Xfrf5!fDZV+$w>o{q%%wXX}OIn^18g>>NXVT=0POHZj4P)*=y~}7#yGlh*k41C=#1DB0nxG`^{kPi_s{YUs5!Dys_m+n z8Iho)aaci|JD|Ktv`3|*Y3p?PK@#Wcjl14i-Flm#?|*yG-}DSBSUu<&rdnuVYepEM z$Veo$m3lYW7`$yUnXkC#)C|DLbbn7VACb+57vNW+&mcM7Gh>1W;ACg9xlu4C(-Ic9 z3`|0Xj?G^Y%xQkX*@Hr%92u1s~%9P(?;H=WX}O8nB^tviCb~O1L-sz)APr ziJU^Lerb${roxIs`|pSlpA|msHHzt9VsB;JIJC3dPk=x)lXb?Gj7uy{9O(Jg^ex48+H@#D zmW5o)T-Q#;D?@9=5UZC9B|mjJchQ*Lv&H1+!!1%TqdL}hFh1!%=mh-CADc)OM%x$X z0fkB~>Nfeh&5x80>LAPMr@4JEXJFFP-^k)qwj?=xee<@?~*+q|Bv z7KsLf8rZxWQ*~ID+MGKIFUV``#%yStxl@wVCf0Dh-lVh{Xw5b;oBvhWX7u@2L)S}LXXhHvAoijey* z{{df1)Sh3ppN<&(=5iNjack3%<{JNG~@LIeRuMR?+?Azk#yyz zgkS>b$NfVR?QtLL%9)-^P7Poy5UL}tBh|X8d1|mM@|7_+$^IykOsR@s=F49oQVUp2 zeLx}YGo|1*jX;xROHESXL>Lg+gDW<|H*B}ozc<$)ql}gjTe-p5TrEl-i<0gztz>p} z7D!*|g0@nQtH&RU4vI(6b7LbwDig=sL}-z9zOx4I)22yC*d<|aS~i3~?aQ^Fpa~ zqF`Ar|D3eKfBrg1!EEs9QJ#wwxO|tx7xw*f&VUD*lUKdKq@LkCe)Q7 zUQ7+$wVZa!M>*=ZsYHZ1YRHsDK4-ZAulU9Bq~BWO@{@~JxH2250MR57Cr>iuy*XV! zh&HgvC5*R|l8SX#+_Fvl{gtEC-mKvtMX&_wNxtNYm;8@lZZPVDI{eu<>B<*G1*rVO zrh-BA`i-k>a6s)+mfjY$Z7+Poh}2j?CnCy>dKlJ&5~IaCbi@>RzTUczaOSOGs4HuL z?)x$t6YLj+=dg4)e4eS%HXiUqS}D5hkZ-pb1a>##{qYkLH4SEv?o!x8Fbqw{ZI{{A zv>Wc{y58+C(EDw=;>hEn*O%Ef*nSgIlgB#lI17J-J_e2Kn$Vv?6p=U*EHJP4MUD+s z$E7_%Y`Iq)5M9&ychxdR%lok}_Q|(t#r>Ms)8ss&jdio0dK*h=8{R~IYec&9DZw`W zLZmh|>}qDfGCG`0RkaUpuWm#Br0)osI`cVXAbM*Pmc8Zwm<77VOAIJ97-sp(r-~es z58Dy+a5`Udq-p=?(=SP2`7s-5jiZWWqRFo3o$pKFoL{OEOXqWzD&9r;!pe09neC|t zB)hk{n8B1U&$5r9zLl-5L|oYxQB8Wr-J27`xop1&EVgxe^}`*4yl(|zFoJPdCzc6s zND^A%peZWa8_VI6h`+K_0cHgA9_vv_s?~tW>H}_n-L0=NMp(I4RW`1%U96I?b{t(_ zu~CPgu3$5D0NU$Z2|14*Tuz>mOQJG!fk)daPGbcF>~19H9T?h_tyOFkGoE^M5~sC; zLJNiYSeBR;dK#RpJbI(nTpI6h9vCfGlK0t$zzXcPE8upBql%N=txhzMzPa;jud8V< zLq!lyF_JW_)yd!Z=Q%SLZ$1qO2C&zOKgDBBaYk!9IevK82o=^VC1#=Ukx4jh7`M0a zY5RBo{-UbI=3fnT;0n-zt3WFG0tv~orS!{1vRyg-RESr+aKhFo7#hgKQDhsC;!^`U zWx~c@vufIB>zohXVRUv;3u2OM$a^A=&c>yxf<9^geND3p7e|NRy{chYa^x%^y&Ab# zQ`*a?%7!}jboGq&-3owX=|3A*Xetv2-P*$TZ`A+mbTXV?_t z_*Ty#eJuM`yFz8P$qRh4qJiZo5kRG+tad@$K;^YjsBeQK`g7&&2$^YrILZ1e>dtzJ zd0hg7TRsPCPi(dU>?Wo?%`%)0if49hX|an6;C9uGW_;zgzy-!#^oBXx-vMe^8#h|m zyyl&9nUk`nUDB0eXZUSGWqvctXK)7yb~`hvE?O_=xVaFI`iX8Jl>7- z9@Y^Jvfhhl1ExP!37w8FI(*Z;Zw_yMzkRm(73*cchR;;{LgAL^#!~l>LFYIgsh3CR zKer<_hWCGVD|my8G>&reFNx>TythHtJVYF5Ga_ih%R()$oDN~19Vw7%9lPw3-Jh4e zD(}YNOI6W7#%9$KW%~%6{xP2|DVkyuWbcmdRTh8?wiCy zsk(k#$QEXIsC6OHjAoA6NJY>7?S9mDW5fAQ@3I@K7Q4Oiy< zriQ!V%;B!TK<%m_Elhfyf=aGSqU~Ux>~_0MROu{mlbuPY3A+x8qfWWFqYn6KUH&yC zEx=@2=%?k(SZ8JzDXGX`x(EVVa$P@}=%=j=dzXL+3nomJO)+#$T+WXHveKmf(`=b! zD`vL}Kctkt;zwvBtKv_=nIk9uQxSQ35OBw=JWy?MqjuB!)OA0xUNw?Ktga?J!nNjis^ zxkN7W#@jzXm@${IedFLwPEJbJleLVPP3~-Ch18X$5^#jLq&f#jDS(2ijYi(^9#tGb9+l8 z)2mbJi@c`sHD2?E1KS&v|GqvK27<85r0bE$H3K!CEIzOOh`^%%wohY?w5F+B{-L_m z)BwgsRFlM%GU8vlhs=9lv^3{n@ZZl6A&c_wmuFCT%aIEnvC5d*qg`HNaL2-mSJe~` zI3f#pjn^#0GuEn2dYm*Q@2RFp0#-X#xkGKRDKY-fRm}aLH|2{xiDj*7$|^R34%=GR4nbwP=ADZ<(KjnjS6}NbsE(VJ4dH@WT$()$i|Ao~jR@*eQozZykhqRX$k7XH@dKX8bhLhuUzzb^IlSNH)YQ7z0r z-~9Kx`=7_tW&b`Yt>snQ|M}W~KN3^}K8G2o9w8<1e?9I0{0a3o;3ZXiT~Lpa{@=g< z|6joWbDuzLSs36Yx^!OM3iSvlkn2hNE@5Kk^fcRf`X)E23UPqu=wM!V0mM*o=H;y zmf^ZYOvgdC=XwwS=3@)YA1+!}wehv8siv%Lh8-MnyWjkDzx_0ns65ZRU5^Kc?N&9T zWIT-9P3Ja{-rUSCFBS-^I!5hP97g{wQ=BxhXyCeD6LUZO%L=#ec-j>`sCE6MmG%vS zv;1^kMkRIe?rJ79r={tv!)2JeEq}qGZF+HLGP%tiKJu{r6koM|`XL5N>why!-84G` z7Wln9yZm%e*?z%S@lcLCx~MeFLnQWbH3!H>Y4`^%fCKjHbs!spN~;lUApB*5t`SD4 zOe_gaR~{krU6wi%ylU7x#WY`qW1+~lBWI7AC?{B<5|v&5A#M>5=~Ljs94kquKu5A zvg&bu>Q6|kPIa21hAiJ7*S5H95DRaX2%qht;XAwalY*DCD7U!WyTZ^HpCN??KQne6 zGHGeH)SKn`Guqz0^qPF1O_9U{T7s?H=c#wMEjfe{$f-P{ZnTYqXckt->C{r%T`R!;ZO?V= z>0YmKyrid|W?p3>BJMvaV7|9dAZUqE)D|#mxqU2Bi}ITdB1yw{Go$h7!=aGsL0U;L zH%Oyy?&`)cUt9(Z?OMyr2jH*9r? z)mbeE@0$qgfS_ToJP-xaKN#-+k%!_*J;)tRpz$L~LX5Oh`0e%24fORps|}9y;AKa? zzYZ5I%5WA}1aR4#53SKZy<3#Jzt8Z#O}a9Wpa3}L?)7yawT3TaEJm)HBPANGq3wDW z!4_qknX61s*f^X*#@E-iN6-j+pTE90c5K=*S^xSsx)=~d>)CHI#37!fd&hkPMi_7L z-eOaMu7CX05lYX48+b?4@aGi!<#rA;B7vvF`E1L7bLnYge`g}^a$zQGsZ!@$tQ|Sl z#&Jy@686wP+I&LXcphH>e%~l@o!N4lTmrU2B9--`c_GEQeO0%;erzKmr2D z>u`hmUjR`!b~S@6V1U9+HoWhDkSJ*Cx&M8!1)SF}XU=53EGBl|m)$A|t+YoDAvj)J8hnl&G10}-r{6)FE=ip^4XP>)jy z*9&0DvtG%;#-`wT;oY0{yS|41{y=u|GHP$bmg6X<7Zv;cp;VW|<;cSPf$Y_ngR#Rp zD^H%EBaWXxTmgGhSKoedpiX2CD0GlihcjzMg;zv+UVBg7$eUlPmyKNpJ5TH5aFQBd zA0G%CjK3jbtT4tr@Z3OHfM|0uoS%XjX`FKWF4~ftevDQ17{!q!<3kZ(4!;5Vslu7}eh z9okgZ_>Ac|YPHM2ZPQ+nd)x+!+;zuvbf5utY9930nLI>qs$BuGr9sSN&{L~B} z`NsK54O!{ZUkDDl_pU&dPd^G2f_~9F*mkx`2JGMZu#S1k^5z3q@dyRnrN^mAQn|PS zM^b1St8Cc#jBbKnw=DJ0gjq&6(^P*K+l@6CdI5A&o$zaI`Y)P3BLB{^sj;>LU;&In zQ@tVkM}CtA?z1ZH0WdPdDc7Uo$TkO1#A3QFv0Wocz{<24%^xK8g-0!a>RH#PLd1rj zyG@)-=W$z1$EN^U6UhUgm4s(mDn4r@S}il|i1tSVXCQ4<=u-vc``GJ=!{%u@Q@)3# zL)X&_UCX^#FgS|bcu3^+WuE)d5(1cy9WM5!o&Ukt4l6{Y`Xx@k_4!TyTHxMY5n6UQ zO#cI&i1)8Ra|rIX3x{rjuu*vLXWKC%Zn~dJI`ON@k{7Go{Ri^@^4;0W%OqG z4L)(N($Kj0q+wp2!b5o!NHqxF6DQ5`4R=)PmZkZ3H&wpuF zBwjTy`lj5jKVq6ILOOqP>zwM9S_~cysklC;?;xQ1*vhRn7kXC?zFCU}IYEJb-8l`S zTB33C;{!uwrfLh2u5nuiD?lob0DBB9KKxbI^}|DQO`+|mg7WzR8B(^u>DxErYx-t_ z7dZXAj>@6`mFD6>T+!A@AvN44k+4eJmMLF&fyOr~ep+0hIkdTdFOcHVb$7D2?YJ8Y z{4W^=$j};efUU7PokZa^wb1w~Rvo5&kl-i6=E|N4seROYWqjle3K>H^P4gfAXmnP2 zmh&7NQj7y#$NYS4u%_DkcO$k|e)s4j-twsH*EWM-dpS_Z+^4X9LZZ6}Dsn&OR^pZ% zbx}>MPTqw!tO8Q&T-`wWz~2F4oL^yms;$itJzrBlBeE`Q#Qd+n>-t>T3Av%DiP3FO zm`leo)MYV^VN;xs&+BtLji*05+`o4NetpH;XxD?|BJo%S z=;roE=!hrZFS_WCA&Bmx-31I&_Y1K}f2Dm^4!2An3^i4>7G8Lc$s|xs$AE&4>jEJ4 zrmwq!d^$-{;?kQ!Rp*Bt|Jz;GD*zGG{4cU_PMJ+spTWQ7sNI|W`)-!|((cx1#(+zy@z!n!Xr))LJ=lCwzv5xbT9vk^T^(?Y*=Li(wnp#{fKz{C59|?&7`FW=!K(|0x>VAal z`d&=SD(kZWiV6FY^|04abjnAMRXkG%Ph9--I62;D52Avo2@#!Ty!^TT>HVXrT?*=RIm z5Z_H5+-bP!Uph~{?OaXnuqd1OwOrdjR2PtP>RxX%JM)RALbrZ0@!8FMmtd|`^gA+q z>o~L^`%k)}u>h4zcfC~19dVFs=w&VJ|LB9xMkdPqNa+ibbOTs8K1JNn7%!`_%#uT| zqqZwJrA_(IWmsPnRSS!1Iup$}pYXCzZSr@0)YZRpwN_=|rlk-Zs%BslTQFln@()V_ z28wnFz&fk-aL-&BzVj~-)>Z5<@r9_#@)3ZRgSyV>^gmp$#~^^r#6M^LKp+eFJC4x6 zxzA(>uyZhEWoQz9F5n8bRKi^mXM?5!rvza z4-6^DJ2K~g7}VNglYjP%GfUV0Xs^7jv>^gQyDE;SNINJ@P1yRcBzAy^%vhe5$OXwRPcBcfgL#YDJiGdvgWn zw#WUAdvMO0GHYqFqzjHoo{@Y%QlIU(AW!T>FrbN_aV0ZD_GNrQ=6zIM@;T4-H<+Y}~$`YT98E{+Wr>fugVT-_ST zO-S*+j4EQ6zF63v)%(&`f{j*n?CrKJRlS+(&4T)Z(`~`006V@z?3A}i;f)~?D z!*$kAoUpyDlzc45FIyJq_wLlT&1c5H$afC`t)E=#uicej{wQ%&rERZ^rQfe%8SqNXcwwjDF29Z}!ierE}K)O^dze{gw~f-xb$qAmU5U&HX%pHO!#O?^-cCM5Db z_I{<*m6%=7-A=9jR6QBlGE*9xaeIbK_rQe}^;$akgVyYgdw0@0$w=KoOZbAxSu?E82rmJ=X%Zy0$FU?{4O$JuwLX+aSQzSlpR}I#>j{cr9`YY*F zhX>qDenwWwf?eGSs@%4_Y4;Y^{2oD*)6MjcSF<@T;0FsdpM%8hsl|n%zl1SJm2pS% z;sz1DXCH*DXOitP_`+tRvkFVh%GLBLo7ZY8wfZICOKhKx@~cGTv-#Hi6P`_Ay+OV> z;EL=EIP;00-ke*)oBRw?9}_=QL6tpeMhgAM0MxUqha;g#p91v(S72YMb&vmmBYm*Y zSoIC&5l97wc14l%$)NfA_qjJsYZ)y9NFtp|&$BIyOJ=*{z}2>^rTt>>$^1#lE}rp5 zRML(7K{}`3d|@nysJrNEHAC!W#eSd^MP!E+u?t}GNSRaJ%gT$9oOeB(ugAvVGY-tA z+c53s+`R{KqJP(+ZPITsgtYiSJwAUm{7Ov-1Z%o-#K*%&_MuZ5kpQq8UQ$S|?GuGW zYOm?p_m9jTfTwZ_!c{klcgXPuNS0eSHQNbC!Yk>bpm&an-jZzn0zK$t)O~Er^7LH_n*0t)bf$v7&ty#UnFP99{&8GCqo~;W>)iLyj))%?fc1KXBKJ zNY$FBgf9TQ;b7j*L(W>TknFpObJSA;E6eDVOHm|zv`?=aYJ!l^Y8$Xu{$Z%Hf|mjG z=dZmK9ZL$J6~y-19{@bn_vC+P5vJfQ9GNloG?#m5SOEWt&QVJk^UjNMG83>fh5#$= zt_sh#Wg)pTh6vF(K)K9vj8@C8{^m+Xrpw4KBODqrU2zD08YcdD(^X{D!Z_CQE(fa~ zqY*wNxa1+xSg5Tp6+1W8LgQaDb;?01hN9`$AI6-DCqR1;62BgQ_gR@UJmrN}Y3&el z$Vg3hYRkixD?EF_j$yXp+gLoI{c~r;u`{kKtI`r18d-ncoT1gjmj6iw{~Z5aSR^2J z$g)sXH(O)b2YHND2XWEQhD`e{jLx`A4c(oGjUZEbzesMw^o5Mjfb=ArZ6^0f&`Hqd7RH)wCfX~yRr*96aK+qeLL zVj}({QV_*tp{AH!sdBYV=Q%9^4y>PZWbV06ilnm{CTZqa`nzd!_!eLweg1$x{ zC!6NUtVQ94n`4}nY1f(;MdoON)z@ebP;5?;a|4;o3nS4mEu?1R>8rzNF~k5x zqN#`|fZD5Pm&QC2G{(mwLD3l-76vJQAU#1JhH;|MfCxd-?X2290F~NOno^onIhO~( zb2Xtz^R4vv1h`cI+u-M4uQg3dWJ2(04f{mp%Flt>q)HTai)LJ`*H{U}Gp8kyao|gn zC=<~<50Eti+=&L=l%WMlU6RJ3`*i&r$;ul*t$xPsrk_j~ec~+ZZLu@3{n4}`b~W4F zoCo!4FGJrebJc(PRDg%=zNEtvvZ1T}ON(W#`p4;FuTvOPJsuY43_FN&*`QSxDu+Nx zwhkc6exZ#^&v@3G{T_i5kP(iFuu0xd@R|_W_5+UQ)Z?#LvFfN}$Awr}41F&?Ka2`= zogHBXj3C6ug;ri*aVJ9o#OK?)*M+US>>jvMHycE#g*&9fLelA0aqxDG?adn*k3tWj ze~HnJz?xS4=puhWOPAnup0oqNd7~kOyHZguA@_YBL){`;1<4m)r6{_v;I2CCEVQY9 zWtOa7)thnz5^Q^LW}T!S&ojJNvtkxso`lg8qpmJQ7o~(RKdttj)@D{k`J^;1+M*nW zhN^l@`m$JifyzN|yNy=Fb4N5LwN$+ysd1ii+nL%!eO5+qi7S0c(!x+$5rG%#ts1Xq z%eA(e0JTQdvJL1^f`6=*k9d1nOL*iLZzMnNYeP+T%W9S`%+Z$V$3x9b#{O`FP=0LnHa;Y>DZr`HSXCkBXHzH_K4ccpGnO zi^InDuZ=7hi-^-uE8J{%MnqOYD?y$0w~eH?R~Y6|e|lebdi1gwHa$hks-zFrr;<_T z9w$8ZFU`O*>jC2|1-NH;`G(3?3W% zRjDi|S&q5PCZ?fa)<8NOs|ct&$4gg|t-kw|g)&3F!paY!?<9~6(dAnX-W^+Q|H|^z z{DB(ssh1D?t*7VBHD5*uDh6m6vel3kMT=rfZy6nnjEBwOy}8%+vGlQ<9-2 zVUv?_YUfs}Ftu47?_i;@p;5YgTd=K-YI(ffe^{KSUnA|y4S0k6y#oj-?f7N2d_CZL zY&L= zU{-2lqjNV16O4}d~bQ=!~T5z#s_#_!RE`h1vWMllFU)5h_;)nuUBz32(P_VB)K z1vWzQB+c5a)J$FD!zsns_dB5@d3bO_pdgWm3P!9r4cS|wa{K`9r8ni@+d;>itHPSi z5|@7&=Y8j`i<)--6zzlj|5ls)cGK$$j&~O-yY9q4mL73}yAqL&)5H^^hkw@x;a6sY zm^ih3+}FM_8XKk#1|ZiA)){r^|U?yOcr3eFOT%5UAYD3GtCLqYviCba^4N$ z07B^lJvR`tH=?OXR}(1)1TROf{xVycg={K~yuyGWyay<}P=M*HS0#a~4`@GPQC@zC z;R*1}CYV>e>x-t&aDLDip2Y(p=dR7{W98!euB#HFC!=Ar8D(!f14-i1e&4-&MOUA- z8&{weqp15HzoY0Too}?S@-i_!0no7Qd146EWW(%?Q)JmO*8%#_ooB^Iq|cAVcJmXa z9!M6KoyR5OsOqG``TXv$-`Z2?tA*lHmnI0Q=<}K+ybEJoILqHpbSWIIjQWMCAaCY~?8d{TIL_ng=K9QCjcZ<_X$ht{K3G8jll9h_59;xVdB7M}=0r*~xNsB7LN za1?WI)0BNN>A{;ilHYN~Y_zUw>nv-58GAO~K0xQt<66Nc0|uhU$lD`}vxw%-EdlWAFQ;;0%P;%)h1C zi07I&_&j+%(9XQ}Rj2YyDYt&+jl_S9!l6)^w|7jM99B8doaBGL^LSjc$nc@MLr=4C zXvJn?EiLl%dUz+~{a+RXgd3c0@p0ZeXTz4~T-LCBr*-XAyBLb_*Q#7Cr%(8`4z;|q znOxnAk0Y{58Q|0@Y-ZO`Kr?0LKhtACNr8PeuP(Y{V_e~eU{hIClO`o}S*nTbYUlaJ z(=b{YL8O=@f4$XDR-TC49ZnW=r!F3k0`0#bn7~g;8e_6DYNzFCe{gik%*Pn?mb#R1 zg(gds92t~+9`kr?E0)aGGGeMKK;&gJrJZ#DGHZHd+I2i^)9t5W#Z{fpY91ZqLONuY zdc3zW6-hhfB>2Wy@sz4;KhEa7-d3BpFN*jf=6pGax72G8O`8U~e;oW?1^a+-u-GcjG)uxA)syRO4GzWydLFA7{KqbwvdsVCH zQ7HE1=E9AYsUR}^m-|jhQMM9iLfwaQXJ%8R-)5-Ao5UhJx!+w{K>eAAn-BDkJs+rg zkrQ>Jv?J>>@ePwOt|N!$+#u?53u_$6puGJ!Xz!Os?WQ_4ByMI?kqE%|{w&~2)3tlp z`gLCxaL=8{{#te`*Pd2_#Z%ew)G>4_?UQ1eFEwO24YBE}Nq-Gjsd+d3+<5Y^F6PKK zJxt8;lUjM~2R_HDsD_AOu6Y_Gh?NTbgR|S`jhlObk*y2`&#T={Zlqad|K^9IKT*dj z27PG>n62wkK6o4qlKz^OPwn=i`8|=5|DzZa(Jy9xg3`klpjSHdV$Ahe&G`_h_E@Jq z+z3rP>2+Mfsc4hg*flG@NG(?}5lj###a*}NGc+FtaVX!>^f|O&KP>078lJen+dmxd zU=|Ja@O`^rw|R&s6>}MrbNkC~rU14tf44TfbH&E5lkJ@OaO_G)W78mFzi8xhChIbG zO)gOM?Qe{aZ;a8@tp;K-40Cs#`6)I30<4v^M3i>GSu2*aRDK*`&qH~p@Fm!eyOx-2 z4M+hqHOm`nPUdIDp&~6quuM*)Qg%q^Sjh1`Op0C`5k_T-hD)lR z6ZH0;Kk;my{Us*I`+m<}9B z*4{&D3wlFyDJek3-URz2P=VpErUGLl8dBFQ-z_Z5D)jl3^})2+4UWYn(PYa22nW9E_7{p=vyxgTOdx{0yxl@ez$9--^O@ zSRrU<+?3&;=gYbHIu@{eg$2s3n<37>(dO5@Q8NK;D$>$k%~@xCzs_%!;h{QmLbI@U z42pDL*V<&NL6n%`;FrgE$`j)Bsa^3QNj*Ux?C@qsJ(!GqWRg8|t&>hldoO+&3wT@c zn-#L74#o@(r@nTS!)i|+$c8$(=M~?6Nx91B>GikNdD9Yt~ zM@+-+_B^+<$n37PMoxQ$^nvnxo)LF@JCJNsie&LC0{k1J z7u;_e&2(XwCznrXfW?e!0X(GzYA0g!#g6^Y&vs1)!4a@Dzo1T*4s=|#1PFvun z&16lxF+wt}^PubIUW=r5)YBA>s#gAO@~6u+*%lS$Ng!kz%rrD z?S=>E6tu&Vpj=l@l&EoS?!+(c6(ObaX|#=aW${U>UDA7 z{)pY^9q;-ZIrUt@3e)%XX7#&l-WyIMzZ}zN-`vyyoX7Ib-#|o>k)M_g^C}I?%^u4U zr!S_adUO|2acusD#L0R&k42=Y#Q$Eku&WY3g<;Tx3 z1GM-dR=cys53Iv0e_5N315G9&AW`^>`NR79TO}%o$P`osAqmR>WS*yBPnWR;L>&^* ztK;iS<(MC|$yfiJkVr34KD_;7kXc=I+jz$c%15r#S;NCk4Wx#!1O0;hJZ8z@)L0)B z?)i`mO1|$dThPl?a|&(kxl>L|MEho-h?2NWXgIdyI$acr#X~m%9=kJB*6bhQiwO_j z1~Nig@WDhRjU7YZJ@=(eecfd0QZigJE}bzWvhIhYL-b8~gkQM`Wv!~?PDkfs z459GZAF}xKT&;7xaxCucGHzoGS%%7Z497tH*Cd2%=?(XZuuVb+!L$ zjBm_5rwY7=mFDxxWX-qgP?kpp=;{->=A5{;v``I zy4P)=f4N@gjq=euT2&*}oGg|iECM{v$4Ei?If0U$dTj|4o!VEinB>S|VGBZV^2ex11hHtVn0jm4~;pz#QFU}n> zQ2KHZBRM<1I{-lPTBsl8HW<*v*D?_8&O9iZ_4hJb0x zxZVnnxG_Gc%}M?Et+1c@;EpD+wtQRccZ;Bn?IyJCXX3IU z06(4m^pZfwX{*orfg1ZtHUW{ z1Fsp?q~oX1(DwC-X5{8Ei&v<6jOYVlutp`y>S9}Cg#%WV5e!J6HqIZ|RWryK^eVQz z6W`cMQ%*_~@A!Q){@MavI~!B=FXVtxuguN6EHrHTnl6p7+;th517=7STy`0m65Zg$ z>px>`Lx&!X5Sb?In~KL4RLeQ|z67x1gW*ue=YI5+CJdnu)es?@nGswz=qrvFsP11r z^;a+*%qk*(AYYbb3J{WQi1}rx9;kL{QpW zFX$9rNcd6AM4*0D`MNB;{#n@Iuc1^@9k~PQ-|NKNAuQ|`0G^qq03jwl{YGC&D`amz zU!A~!8*EzjjsxTAYbzji=^1K%4p)FU0ogRTyE>wG8TEcA@7zPPC3F`#s0OjeDpFhq zM5fR~ZU}p}U(_9=ziBx1#$qhs<$SzdIBI^T&M#{!b1Yc-1qvb$9NgpJDPkhgWU)&l zA?L?PB4@4Fyd~~eyqHWb?AI@|`!OEEoWyg~-~%`_AvC<5`DS_Fhgq@Z=-z~l$68Wo zZue_-VlLeTnU3Ar#0q+sbF?oc~V3_s_WjO`~kdgG6-mVp8BB zzwDZ>C8x)uF;4Az4NmO+ffO->7Sc{q9a})Aslu{?pA*2s!3#6}u}Qo;Rr2v!`)@(f z{tFT;LrDC8#tNns=$)U77p|Y0Sz{JE0RD$KS%5@1nTz8umSy(m ztMRb0ueao!lR?JD)bmw=2kK(-P!)D~|Z6=lMBO*Yy9f2=Yff^jPpR$uX1$4~@wU zWa0Ak^WTXJTRSKE#68kiB@*8&SU6Q}T=c1*)}8OmQKnqzo=#I=PD|qq*nY0Z3=#;< z5)*xH9{eSbeAj80`Zq*o)M#tMXG6f^`Su*h8a1$s|9FJW)uAfGrDdaGa4vgOUh_1K zHt4<^d5D=+y{nX-;HNl@@-s6(QEAtS^@e~P2K|VGXNGjHggN1}`C()JqVhO0h3)j( zHg-R9%#pzdM06=D@8bC*K*)K!fK_qbb;Vzn(&$n4M`_RsgYta>=Z3&k0#kgkL^GNK zYCNn%Ep11EJsvvxZRF3G9lDNmYyNkLYV&zGG99`SVcJ~uywMJIt$U(3nofWsB~oXO z>E3donN)?8nv8M(mY!Q{aUyhUqE>#BBN2$|*`Wuq?Z~k-PyX0g1aBvkw@*1CS6{ry+y=V6DqPz~0r_Y(l+gsjhW=Vejs(wF7#=~;U6URWxYvWlv=NE z_5Y6WVPMDe%;6ED5zFs%=fb*C(S35ioEY?aq;6p%!aA{QXxH)}x!+3KVEiyRS)@Kk z!dNwbW-7!44uC?<8fJNo?245=^cCjTD-NdGP;PFXT>6Bu$-m(waZD}Cw}r$a4R zN|cR9Wq1_WTCFNNZ=DX`(&B+k<3Ew|KI1*^Uc6jkm}$t_d>8yV_E1JA$K|&5pX-{P zZzUckw3+ivPU9~3vgwAvU#p;!3^~N5&Sfm1-RHZ6sP8_Ec|x)%3NS;9pflaCtg~oN z1xZ8*4cgkAn_M9wXQl7PM+=n!t&AuVA~{vj9;O>H6j_j97O__%#a8C<@ML&Igo+Cu zH}#Ff*vScPD^(^s=|`he*RcT6XcPRzLjMkKe*;t23++^})Z+n_zgE{pFAg?yvet5o zPh=sm&bAoS>xM5`(W5~|JyR3|cy>W|TfC`g`03hWI=2gU{_PeWeh+nRrXoXkSxRL+ zXpc7_8Bf46PsCB!pgKvPh;-=ZZQNRCU%bH*>1Kf%BEw5WeN@}6L^J=TMLQ_ZtG3eo z#;*uTmfRGF9LrN>=A+yuw!_KD22qRuXC&;0cGa*5!VG%zH>GBkNslpi7*y5yc#51k zb#dSx7IHloDveY5z{hCO9U^z4KHGNR3h&6+ZJQ6HMHe}_3ynlVyFNs}kjS|w(^UOP zd&1gwcH-kXjh}~TBglZgTjiA22V)Ge_AFVa^IO#s9jnYH*=>zNa5kAyjedlYWcicC zEA_)p{CW)Qx98HQj>A?W=36ig`D1+L^7n)A8zYvljR5U!l=sHv0_`AO*?XALO?+1U zr2%RQ+ROftDL^Mun2jjaQ?iPDdDodxRg_$|gG-fPpo0xjz&MF?b5Tr$XXd=ggi2ad z&||&)L-B8IX~7M+lPtpKASi5zulvN{5(YHfikmH*WZ=?y>}fc!_Bm=z!(TFhGTiJK5Z3|QHQdt!3 z+P{wEd8tM7*n6dM9?8#lE@-?AYcVpdrAUqN{@I?P!x^F(4pZ9YTT=pOJm&_=jJ^Kx zv2tCVG0?az;w~l2JJWZhug8X|mKU#_^+j`y$qK7gb zo!buoAH5TmcaebwY&qJu466!p-0R8jJM>9;gDv9kkd0IN%OZIp)*!(_)0ceLAM|Ll z5;8+JQkMN@`G~f=oHB(?_DzUe zpJYk;|3yh}ooXPEUQ}O0qq0?3CTdW|l~Ns+UU@YrPH^8megvNcMAaX~IIVQz$YskE z(Ap|J8-FTk2q>p!b0)`j>LEK|^*-oGoLJ0|%SLBB-83Sqo$Y6WYGAEdE(2q@lUNb6 zw=!W)bbI8&7pO>=V|E=kXoy?G&_cp}*n-BvkqlqR6l?8Cx=Ok$@}XkBUAUJ?e8Sk@ z(M%mBPULB^6Y_TR@DC@${j>CpfqbOC(!5hjlimkhoTmF#wLf6SkDE&g+t%j~&cEt7 z@c)E!2|#@y1syMi#wSWh7Nd~be)^JQL4io4$@t)IWncK=cx zesz6uB?XVLA9assRLJqHL~1f591~jnRatpf6)WZMS{*-$tKoSLD8{SLDl6WqOhm=B z+ef!NBGO8q4k^Jaa+%)b?-&OO%@kJuxt;4p*3ndXWRY)%gOS@5zt4NU4=)ME*zcyI z`E=_@9)8qbQ~OG!FYD@`bnbM-%WOi>W1Zh{x&qEF6c(YS=Xq3L)H9n8zWC zs9kZ_^Q3(*_Dd)6fb6uCe!Y;RmTR13JOy(wLvbxKlhgXGX#LXC3GROk(`K8eT(zyV z)_nRf5aIeW6O5quHWl@nF6;whbmD<0MB)A9gmf5KWf+vrbnXHn$%D#`%F)mMdHRG zY$)o5hz1`0VGwPtTq}crSOBMdDWHd7Q{BUsV;^>D0AbK|$F| zJPtNH0*9X`jW3A}!zkrK;* z6nMN3=*XfGwY_$GW5G`oY|ZcJ(20%@D1~f`y$W#vh-NBZdV7#$;5jV|iKk@A_qz~7 zok@FqJmnG5W@(QxIy-t-Ob8@@raDzT+kkL9Pj<}$6>5{2Q=ew0&{VS#uPhq>J4LuJ z)=k7%U^3$3umkWbv@lf~w)!tWsMY+E3QW7%401AV2gS03)bcdiQokNO>AP5W%GL&>T zNQnZ{Dj?mBfP_e=^w16GVY~PD?9W-}4>)J74?nR6);sU}yiZ*BeO>o;@2}iU>ymic zPE(n^=KNiK;GiOI3d^H2)4Iu&4#NkEQ-^}6Jvf5VRi2U`JWkN)dbiyp9YdWXjL*A2 zzEg^T5)lLtY*4@ILtKAvYhcM+jv?!y7A1SVO_uD#28$PKmQWs(HL#hrNvn?Qy<1XJ zW~1su;pd|JZHEBzB7wHP*xAcVWOHEntN7AO&l$>V!s|?m(8iy1gt~bGk-ui#@agH} zFr#zLf4m{Q)RTaFIcOj@K+ytS?291l)2t$Q#hFrI6!;obwFp=d$lZ`en|*vD!(J2& zR~$`s_Azjb8^q-J1>)eyvI)h~u3%%Yg}fzpL-+k#lkFLvRHdcc6WE0SuRh>5zXW!O zuqg{%v97$XS3i0&RtS@B&QVHr^tx#sj)?T*J00GJ|Lk>hO^=_Yo4NT0tMuk-DfF=s zC(ZGf8x*C+JL?r2B6r;`lV(UY@GV7ZfxF>kxB=Jg8O2-e+GSvwH$WdYy5pfSsbzNi z=_wg+mzJ=T;6fti+avlh19DjCC$Y)Ia-bN@4VCQioR%dwyTeGWG3s`5z0tg_J`6tB zc3`EkD>E5xlxMrOQrZT#_bp}oDev+4Xi5>jNN4pi@?pVt!oX~D!g`xvD2c&a>LYdXAzdGAqxp=%u=ARn?B zQ@iy)m29AG@VB3K2fjkd^U>C(4L+FH-?3|`K&7D7vtCTUcR{zv@2Sz%(ih;Qtq`ru zP}AhnEs+v>b$(Ezcls04ub{MYyfi*=zzdsQgqFTTfVhAs=2|~cZdCyxxTJ=}an6Bt zyf$`iqEsR7yZe04f1T&{XC<@L#&~n(2mrWUd=B+HN^zr`-Qua_**sK_PF_1|X3I?C zc#&XsKG-REC(}gV*oWSn_~hGTqF)c{jYgFv0Jp~10vo8Eo|KU}3iLZqm#}qO>HqEh zdyAxj+_hpJYhI*h8Q<-mRPAEhT6GHVi{fA9J{(e%4Ma?sF^S(gEcR{>1{`O2=gr*2 zN_OT>c}m*^tf;-)wig5yDv7B4+8pJ>)8XwmXeEu>>0^!bU@vuaoo$l1js#bU8H6le zG)Vw(NJz#O?=F;n`J&dj7&Y!tAvtExFr zQupB&f7o^L?uMqmD7(%X&sdM&#;z`PtQ^bOAq~H^>JO4PVao3eeqUgAfpn_!gWoF4 zK77qzYITpRH>ZExAAf2)*;Wq>ndr^Xm6Xwm2d zjN}TjBN6<%Sp8?F)GW7f$Vnt4G9#^oaVK>Rg~>lwgP)TwB_bDf|CUr>t98MrFN3df>MUc-Ju_*TajY4i73E;^Np-%X<_jKu z6FJ`@9vdhi4LY^b=;3aNU5li@hF3nmpxUCptO|1AeXUN)>+jtcq7<{%=5Cifb1>3< z8M6a9X@G`Ma?q4qH+zt+-as;KT&QP5u&|-;8jjKWLpfZPOzO6_HY?$r?T6pHHp>l~ zkmqcNc<)~HQ_UC|x*7ony@I!N=YlltRWDqX8V~7ro~%q#SfDG0S-V&Y+j(J{YtfXY zks*=1>Hj6mI-PRNfxG?v^!p>x7%mL8aqDA|dCNhs>3b_SLNnnuq8)6Xand)#{M$`!#nF2Uj5 zgO@*ZM4kH7OzRii4TpAMSRKE;(>>3dpT3C>d0#A{0GA+oR%ba^8FCT%ianhhKO>{J zMPKP<+b6jUC2~G_5h?)$^^OE(u z`yTy|aBy%)uqiPK-eT%WNj-^XCpDCd?ZHwtbTITf{5bDk@8s^j)VJQZI3;#ov%H>` zzOuQRpXX+^Hne46*SkI9lgn>0WP)T7Kc7&wS+peAaO}Om49oxdTYGVMbV`X>;zCW| zw9a*Khm1niu}`G^xAq6W0?Cu#*)~(I%MUW8MPll{mg%?{AY=LOQbu|G%^=6{gLH+$*4XSxPT2UKJ0d+v6>HX2W{ zt4msIwYy=PY@O~}eaC$D$H$d0p{b4yeITU%ChEdW{N5-6zN55R;7_jqVIz05az4BQ z2q_oM>-Im{<$G)FGku$jo#wtU38XDc)Uk!^3?&(ee0}_X6`+A3Ir)TYS-X5aMsg4fbh7O%9 z*!9=aK7SnAu|BL6!u862FCilSsq)9%yjdv!w~dX3GwXD3kq1;qm)5Pm{3|lME+eC% z=5(H`RTY{P%N6;AmwDnREsofi{e42J=JoA(s~2Y>#qYJ^4a~+aB|lUX5~Mg0!Xp@yPL~xG-9r66!zXQ< zDS1X;4P6Sg22PaTE#v4DG^d+%U|A%p+@}(Ie`Cb>bA(&td!={jK20U*-MS}x zp#ptMY-z7Gn>al|TS7~X-@5r3PEp`;7?}QOmA-TJkE)o1%qXt`E!W+ku%Zs9! z*g}TxpZ6{gChu|!AlCaQsUOpS>ZxSmv+Rm}4Z6`Uo_O!3Osg5ZC~d8@Rh85}12a-% z`r_d0kXZ5ouuw!Qk5H1dBwy}|sB?}cgJFN^=<^Z)+$f5+s%^W?v4M+)n| zJH&r?&Hp?z{vSIVvfF>z)$VI20};5JVScNV%nT@?)Br)eg~V&nYFLAit3-+|&$WjS zN2N%Wt5h!8`{FoIq>=!+a(5F{W4&HZb9w&{YP3mrT~a1vQ}4MPF?8&$I9V+?JbFv6qiA}C2tiw$u5S{% zN&Pv8b%(W1fya6Y;OjHa*R6-XO`mV_s4CgCg;K8YdLOG+Z`GadxZu310^;<%!`dxR zql95zHnkK%?(JXOpw`nG(PY;KKtRUScDPH9JcZF~L&063Ia94aYUM5KIGtFQNC4%i zE91^+jEybtOZSUOkJXQPgFs;x9pOIuZ7R-r3=uf)sIb}$nr}#4`5ROnF-5@d01cuMYr80CoE@q`EYUGy>8= z2ka{LfK}N0TiUvr#kJzOK968TRl`!!bh@eow29@ktv^2yxOG+XYqcFj z-}aOjhzea3%MZq%_rLB_@&is7J(>M&72AC4m~A9`;y6{J!{ZkHB$x6luWrz0?qPh`_VU*UF~OCW0F(rpGS>Vg(mD} zlYwM-NKs-Z>P-I&rUu6)vfllR5>%yDtpujEmZRtntZrYYwPvi6C^944^PJ*n}n&9l#IWPrYdfAsY;!@=R zTjkdk`Lj-cPk?M0Da07|`c(+v1}1@nGIQcFmg7%?; zSH<7Qcds|U+juU7W2Fcq%~Je8dyvcP$?*xSf=R>b;&7H|pFn93UdjEB8o9vU>1*-0 zkfAv`Al+l9^SFe@W#ZKjS*4Ho^|NR3<_0gTEcLC^oOZr={|&q!Fo5xp)(~hwyz`5a zM!)kTTK({Zw2}ULXJ3aiY`3VXlRZeEjW^ zq73S$zz^{CUdhtuv?wV*N9Dww3CI4$D&vaz^DS@h0}P#hmKTYk6-XJSyK+sp#)gP< znyr)YqA#JMx+7KkMCb0HR(!b(RuK)w=9iZVFWXv;mm|>rk>WkURHAPgrxieha$LV^ z?F?!^XV(#|B>16KJo^zZiqrb|b7&^8(IKG`8AmN_0jS50mrZYTv6Nbx4ujk&n}^gZ z1*iGP(JLC>ZuocLn?DkEj@xVHGj@pW3_06+TvA=8Tph=RX2UBIT$^f|C=d;Y%n&=V zVCAR8)2gd7bb}d_w-2{g@-osj%r|KcRq~uc@jP+gK+Y7*Q*H97t~u(|?ennqa|7l= zQ&o3UCDtc>OjOS}=ci`$iQ#Cer53vuhi>i!5uNh9!H7LJ#4g>*_dtiJ2|UAygUjs* z@1Bh9)3z;H&iV9Z+CqVokga=`O>a(vyv(P-MNT^=6i;HDhm1EjC`>$Fp)qOd-7QeV z|4b0Z??O$HE!=0;5%dj5{ppx89Aygm=2uIG_$^0~?`Wim^aidNwW4zy$V>$F#~zpC<3+lMrl{i%||uePKTY~^f^t<@%KHl zZ}SWnCeEH>7I@bB_MufcZ>wc$wm5HGec5qi2vCBMP4AX@ zd?CnWjxBp;p}yp9a_Bi@e9}Zw$0zsXVg^sLgIKeJM}^K54*PIQt6i*B$9AuN+#7!e z6f$v1E^tIxQk80C#Aiu_&oU4@ej@SBZA{;~Hh24%%d}@8yplb_$>2}e^t~A8L zMB_XXn1xvtL;aq~;W6I+f}}M!RriESK<1jCUnACiB{?g?xXac)qlF?#H?E1;>n!@V znY;$|MmG#vbv2-~O4P6xXNN^^=&v4h7d1UfFGn%qGI=efW*JTiB`5%o_mz2u#{BKy2uoQ1>@-;dMi3nTpv5 z^`HoFSFoPVkkF3oka(I?t`(H0NDg7YG3*rd_S(y)yHOT!&9imu+Mzk8?B1@J&#V5q zxOroX8%mpYn+BxS4L)upwGDPkFnFb;4b^EC>mgPsNWIiAP7hS$=uMEpG3HP9@EEU0 zFpo-`&Pj76uXo-JZ}(hbvh=};;tF?@M%%!y5yueiTc>;D17sTF!uwf3LncCBhn!BQ^$>4G!+j2B(ZIGzdiEH z-G^51`hCKsZzv#{(pz)|Li_VIYDfeiFPrsy`BRM$K{~u64mqD7#^&$3eL|%fuvJmg zd2KUv(v7b8x;S#fFr_))kmqq&e!HtRqG-lf(0~u7MLISl&y^)>7nD%Fs{yi9aNSStA24NQpsuh6pG5Cm|HIn{CKo#ifk9Z| zvNxmmP}29(e>glk-oaEvqPxmSFp)M*Lq9eS-}<`t@V7kHcOiaq8it2~E^xCXDXs96 z7B`DL&02Nkz9yFjB|>yylSa?R?)wS^JFsV*2Pq;MEt*vNMdtWrOw2Fsizmt@Pk;88 zJq$Lt!Di@!pRsR!9E-q^y@^O6LLQ(SUfJ!Qk_ON(30*5-Jr3RyvYgK?yr884Me(k_ z$2(IqhhcL3$d$?nM6b)Id%9@%n~XPKPPFS9wQ1E1o{`h9AAQQ#w|E%Pu2Z)Y*El)h zwUz%SeM6d5 z`^K*OHvs7JkULW$v0IQI9q-=`6XZU%+y8tM=7jw?T+xsZx)kTC28k8>TxLx261E(` zXYtT&#uv|Oo8oltzUKaJ9lRdG-;&)v^Ys~UsdzxNY(d{wg~)ySG~qi_kx_cTPs(>m z-0w(65@{!Ghfh=HPVEU9PMi?Rce|#U)D4QR$S!oHIZFL&vWDUqXHChS7-#@~sqL4a ztaOhrGChj?K350gtanhbW> z#E0tw8tzGt04tlHn~~`o&8m%3Z}VoIcDpJcI{A!;@x9P`gnM8ivT)1%p&k)zng;=! z5?^h27wYPKwJ}@YZYZrMIv%m@aSyxj`uuBBG+aU>ise`|stg!(O-#is1)Uf$! z)dJ6=eV)KlUU;ZP*en|Hw1O=@2;mcP^wP2?N$$*im<50CYc@}6!aTvJ7P1Jf<$)z) zyo=OrlYTp`hL#I;6M~I}Owy@?yM;9;;zW-$s*oxq_QQDc$fIzHli$>&1_3(lF6DR5 zT*q3(8$IRsFpc$Ta~|>3v!RN13`TmJ$!fZgY^BfkurmDL6%n>td82;D&wi{3*`k(7 zosQG++KHh~4)bE|Ne;yLQT0M-TB|I~!E3)pIAc^-BB#cxYWnZo@?W7#xGC}cHPe#z z$1h~!n)2TuwEY>dnNjS;XXaivny?Cp-hi>y4H%VoceN~0uB5s|KAZk1ucY^N4gk>& z6aqCe8+RurxuSs1Q(2cL3*#3Ev&xTp?Qkz%GeWxWpTlPEn%-b~0^Oo3WVBISpC3o< zljJM1Gn`YCBWfVO*P5X7!nl2q_)<{Y&lPyNP49=6fDo$}god(=Mv(_>oN$vf^46wT z?cDcP7oJ7I>%ZK|2_y^1&Etv|&qhrfNne2#XuPC9hEtG3NUZW95Y?lB5GZbursfMB~Z@_Si*0pjB^7WW0pI%0D8 z*zJdJu4Bmu6R*c-nHLq=H<1>ytP^BR9`R^18AXy|IW>Ee<`BF`{G*BH1jlXWL9@O^ ztYw{IxEC$=zXhuWIePvbr0-$-zT$D~-@^c6|pbm|~s55GrWbv<1Z^>DZ=kf;&5{TeVzI>fzO93drk@VuU5&M-WK9?sVB z;QSrxFWDDPyZe<9+kFV9!-p$jytShfr;B$*s(dalGVPLhyU!c$t1`vhAub$uO*rOm zRSh2QE#~fa{NG>tfjxd;)iQbSoMlY#{SMktU12 zW$fv7+10k0*hN|Vu!4XecM~UVzfR&V^T`{m8Z#-&3(<;qaa>dTc45Z`QAWzLBS1kU z>~bdT!`sChZrqTMl4dXxAN>5=#xyNX_g~=$2jk}zxaAZqkJd)0eOTvQPSA?rt{SsFAezr*cq)9Z1Pse95ki{jb=leF7ey<0qo#Nl+j zM!6pFIc1Hk9j#yFu6>s?;^QwKyo7Q_i0h2cQ@KQbe1GetlC0EkV2*WA3&fw9D&HtG zniE-YzyY*-lEX(jIP1(!T6+%r}*I--3|CJrSg(FY1~YK1u#WB%x&2KAUf>9BiYQYQGa zqG7%sPQLEs^Gls4N>tbmxIN5oXI7ksVc+Ar^`6Kki;@J)ie#^A2lKcwKng;JMsGx; zbD}yJUkD>$?X9NihiOPR;UYD&f!-bbT%V*{2$*qz_D@ztO&BSU;u!TIZ?KK%=fUn4 z(^P5KJni?#c5I?5McEt>rfz0pdHjzp=El~!31zn#h4q^vr?DZJ(_M3zH?laQ8E0LA zsY%Q(hk;$K6VZ8LdVe!cyg*|YU(emxilHq|OF0>)FWqRDsjuaU#qG_Uw5hS=KaCA~ zsd$=`&55{{71=qKXKP=y7uV4CnCh@;itscU&!@hmm1-5KQ~ARXp7=^l<2MhiLTV5} z5~0#}G;%4wljX~Ium#gVKh*?8ao;4N)V8|4P_FdaH;;WOvUP&-VANF^nct7WhMT4p zfdKxkcbmHX+58>A5_ZgBa^IuI13hqugy`_T z?K>Wl{Qe0Q)T_xI{@FBUcuG~v@N-R*2Y&|wH$j&6=laU(&{COj`cnZY=Z(f7G-vAr zi29r`4(7*M%I~Bq9SuV9>_O%osb5zcM4cfhSR%=kp1u|={&~tP>d|G7cn&F~TqT8{ z_Y%K+yi9oO^Tq~(Vp7`RXDKQO*jxlAc_$a+dX%&gS7@JU+~-cSnYR1mdK}sxIYaCe zt)y|wdm@^LszAb<_)GR<-+^c02vpN6@o@_57puyiMKwEJjo-_S31xMo__Di_oRVFBHMBa$pNO$uR0jfXtgFL9($6JhW9 za(WkCuoy}}c8-|!b(1Vgho83NhwGSvEG%AzFjIpLEzS%leWX4;&p)3@>e*0B;Tf1H z^8^t@U01$+&X`GfxJYRN~uXNu}PR&;Sv!aqg zUQ#$kyw}vf@miA1&zIKt47dUe&*pS-?^~>x6THdiM_M%a2l`5rU?+<Eb-sMoxM23nthApV z^Zd^j<-MeDC{v6Ox~%4i?|m4tUNX9`9vm^oSE?8LGi+oQLkadcv5nvojo&QfSrt$Y zHV|Zz3)P_tGrJ7e0B&eMx|;rdb0yE4do4FJ94}vOF=)De@J%4hlrmBd0ZGk3yxX1H%IN7OI&aO8*$yXFKI@83lQ70gDSGN;73KSN4`e39?op}lG671#13qC%N3a(5n5Y|3u7AEh&3{p>$SJ;L*nWDKplSx<5v zNqn|NH*#~~Yu4~d6j%G3qXI&qWwAi}Q1_H-G(GpGEe>rHaU)IJXXMzeLkVM-@g~Vt zsY=1jZxf2oi>j=eQa=6j0+c{vINNwR*o}*c1e^D*6}ojM9TTqczQAv&60WhTT+~2z z$tUv(wjifV!C(bp-yZkpXE(QRo=L!@)tGnvL>3T4h&za+ZG!1O;vFI6#Pchixxdjg z7Kpa+>D{5reMpGtcxC8!e+=nHx4a{Qt=-p--E~}UhH{(zoORCMk!_5mh2aMrqROmw zV4Aqtua@q&zDw%=Am%9(AbTJ{2bEUoupL`RMLTRo!co$a*dDm~2uQ$+0sO9EicYAA zv!xleMDr;s4~Ga6+6=9NRFZrj&Y`3B!$9$OYJ9*BSSD$!pvr`WbVq#Q>xrW}^81?V zPPFB+cPpI}rGlDkD_vZBN+7%H(-15&u;CG}Cof_P zYnpdc8SXobr(r3Ap8ZBBP*(t}SDqL>BdSmlZK+iVVw z8AvP2oI}lz>k>MR<@Gh+8Kn6a14(`a9y|%lW%)ozY%}NLGp*M&dQ9^{mAd&5!|wSR z>$d0Y)Qk%y$eNlIJl9^E`6pbHBz*`Amnxo{Mey~R`uid|`E^}v`YfgTTp>+sp=_7b zejm(zK>jc`upy|*!fEbZ4XdBxc#~;4B#$e?=5tuktnx+SNV!&35XCATA`bFau?vPH zkZ6JN*2VuzXpa0`lJ-<=rJz$2;hT+`-9C{YLI+wVRVGL6vN*WAX}8bu=3n-$RIEpd z5=FHhhJOjC@Dq3wh9|vI*BxaWi?)&}Ev=M1uKj&*+G&S>c)uQVW2q>fn#9@T5L)U% zH$MCo&ih5=@W*3~Ub}8fVKZ-`;%%{p!wkXKJ7Q2`<$%-4wfD5NoPV)Zq`xV^j_$Rh z{A+*$M~U{0i+SY&9I1*X65wm+6c~K-nRh09R{{F6&rqi3wFQbMA|NmZB{lKY;&S~- zWO(}*ed|9FZUgd4sAMN@<$0PcRoL0-lk~8++W!lQKdN<>X3beSq%L4|IZO-`?v#J6 z=TY;hxp!sw*Zng_xv+WeAo(AugL$A&ll}Uh|NSVbbOU*-(ZuH}m9&mek^~fWd3Ou& z+nb?b7M|;ZsE0lC5f}_hTtdFKfF82}l^T@ZVXzPU!C~mzJ5qk9`hUYHCDU~%c$H~& zC9I?!IEmOQI}~L)(P^dkminVC0-33}7_#pBVFWu}afK7QWu4=Py}w-yIQ|R`QjCBd zGOa#ttcqnsN-u&>;Gsm@eBzIU-v9H=e~{W%e4}^2#>#H&9q8wN5 z>e)PP#$LyZubT+i7UDnE9*o-n>*WmuC%4LfOsd~PR~-5N?<>Wky2SB<;rLVc6*jQ-beBi%?mtvQt^|jF2JcfLkc|-Uf@Y?t7cC7)6@&Js}#f-b)m0{~^@X)ze z>Q`~Vq#RGQk{fVwfBvCq(|SusZLP3eJsHS;15<}qQVNQy?@R(d>==zrBuvi@RI7e@ z7oZ`|m1I~4B)P)|0d1Hj>3uXCI0X37D6(a)(iYNS*I>?16@xP(X)oyImJ`jxslVJ_ zBppUPtrjbtSZz&H(E-ELz)H2SI_CRx;Jb;f!)4Cs7CNpL}kIl==5 z^)+Z~V^L}kcmxX)SxUQ_Z3WS#M01B%fRDE(r>j5Tuq!{}DE74nCoN52&@TsXC-*B= z$rUqB{`$(&o}3f^`xWUnEISl$@G{9?vugfInZhLq$T!s32_V>FE&zGCAPemR4|zB< z8^kjn*imp>W~Dsi{KXriVJIrW#LLwU;NARh0X2#fv_MPpm+)iyTfk4gtwS$|yvR z9syb3z8x^jBD;WK{&)-?BNd8^;Ii}m&^X^Q?uUr=V^8Ko$awr1SOfHUe60$yoJeFL zveA+K^6FsT0>t1nmjk+H_I`GF#r*q}mtCg>k}QzVWb-vw%k20&aFgc^;i!3rEE~}^ z51S~GJYUfuh!{WH?av?*{r&Eyv55NH7ezq|@uyM`=WLt16?9)(XvuRie&E1sJOfW8 z<~{}u0Y;W7Zf*0%7lI8;LFM+&fk}G+8^ZYONI52_2j^?t&n8uqm_fMQKc8O!NVx`gW}5G%#qiwp#&wZ9>E{@f&Jh z?)K6?=5!f3&RtoO?35Q}%30~K#Z<486}>TUz{B*&=^2zNRQWh{-r( z4k;k^Li;7T)Qx#xM%KomAENk;8wamCImsn*ce=A@p!K^HDgtKDfj{F-p)pfGxl`5C zE1k?$NJ`yDB}2NcYJh)B0+^x&#Hjr6Z!OwD{I6Y` znGb^LKg;JMIyo84=K;!L27g*-i0d*VVvr&E0WM+Q2tb@Zc54T%vh9A=-M!p&pFTB~ z2oUkiO?Z23gJKF8_IH|dl^sLiH*2Q0x(@5WO@j17p=vm!-4?M5zuD5ILj{MN3-<)) zwwtRHPQ`RA5~Olj?# zk_aAy9ZO`3v-Po_x@!;MP(+xS%i%>6JpNn^QTRFeK244;djpEg_T8G$gee7?dkD>2 z*{HWbCYN*^opH9nHWVnam$|d!_v>m)a&(|v+M_|v_#QjLOFY7lL{~p#TahAq#IE)Z zi5P2gUc;W09C{hIq%B?j&J)LsQ$wQ8uycHovgV5oJMZpM3(0hKNCbbnIfR%AJ|5f* ztqIa;u}o59rj>Y>c+ zKv(xjqGFWZs4w`CeAFGS7BuHC5 zYI!*<17)&cdypJU)(?$Wd9PQ~40Fn)Q(;9qafp%5(hCzfvQWJcIlNmGm>5wNX&6$L z=&e+H@88d~_Xcc;6gTm@e#^lW#uyR`Spz<5W6$~sH|i6_tcuh`h}gc+k$tG#Aj3#b zn(lGjZL$=Zv<~!G9o=kC%625Ju3YSLc6?lbwBVJNAEFg%vC;m7?%REdC9$CTkVpvP3H?zqy zD|>9Y;NLK*`bsPnpq*k4URDpMxff-Ct|^fTX9&*Sb++g1nL%k|K(r{3s8=DH$J1iv zw29E;p?uu=Mz1fJ^KjI*om#ksHmkU=>o{z2{O#7HAS^oFf3RMRs3drd`yx01t8^(zg(89z9*+hf1Lgr=-e z-lI>Gyq(6yu%>sHFv)r>xD_rIE`Ek?cDxJwk~H6*D$=4@5cwcH1gafxNRQ(n%-PLS z@U+0~0%+DWxR#9fwFADAlwK*)J{~wQXs_K3Y$I6os23%s+r_s7!B=W1Za;OBIa3JN z?(G8d*%TxN!SScvHv!+<1Yq4!Pr6t3#6v)p{c,~NO_XOw#kz~UuMDL&uyWghFl zZ(on)9bc-JL);RXID{Rvgb{GW1-RE+G1}qb92hB7=Wqa@n zcXeAu7T1E=(i8DTa(Y0l>1eN0^+dtwH#1&5R=6b(z6ecUmqTY_@b^1m2SZllt=|A7O+Iq$B&&f(W_{F*B<4j7j?0Oh0n4E|Y^9ar zrv{e9mL|hpJsHg62$G``V6(+cUfizlPdf!Hj8{Mq)z=OkQARG?Ysag)S6NwfTNE+w z-q|)C>@#xyX{nU7SVnzy%J=o?xroa_)8)%tH3fIMNf5~14+855g+a+qjqmlnOb;ZCtE|)D9 zrWj~+b7VMrYb0A{0n%wFoiS|I+Yf1VUNRZ#mPcHh7W<5T^U6bOJ$9GMz%?<^&jsu& zi``M@WSK&#af=Rc0wdi@8U~o27RkLJ)|O{Bpv7VDl$-<=4>f5q)ofa0f*dbWi%FF& z^8>2PVUiC;Wh~yp6d*MZZ=qlmh!2_Bz3M2`ksRGl?Wjg>wXtNbY4y`r!7^bC^%70sc8kY(^=Y^ST0p(G81fJ)O#U9JFgo*)_@%nv|v2&e|tIy9>N4N zq98ViIXYrA?AVR}L&Re=JAWQTg9M8ehgb%am}HqAx0?U1VHpZb+X$bQ0fcB@t!97R zp;`sqLediC-c31$G>#6lz;l1ZMe3A=GCoB+GvFtAD;lBSX>*fjEbn?A{A3I4)sEnp zCj0W%zIE+wrFpMpC8Mw&Udg=^AE-9YZ34Rw*i&tHEp9GxS^9RECE=4Nw|DXNs>?i1 z5>vSgyBn(@Nt&g?&XX?jr5Rdwh2xrrZqGL1E3dYt4y}5=K`KlPS|ys{+l{~{K;RdE zKU*14{2$;w9?WP1GaBj$ydXYBxM(h3!SF{Et^*P6*XnD125Ca%67AXke0N5PO|Ofs2&cBPVY--SLxN-{yp-OlkTx&0CQiR#l#6HkR`;YA@0AdCL+ zf+5-9GgDt6M`l&5!S&v;t^YVvtgcd8&c9t>rke+v+6YnD-Q$z*SOUG7qUzB8yg#%v zA+;>b0!s=bwCjQYapUG7*q7Lx_?m4~x;bm_xIGnK9XnbH`hht{Q@~4NSRdSuK8~AL z3X;7`OFG1NNxUtb8d%Krj}GGRikY9F?SiS&P_^Y29Px%m@X_J18C u;MaGO;4-J>Cbn_^KHHh;|G&TdnYWfNDOvi8haCp^QI=PiE0QsN^S=NAmDbGw literal 0 HcmV?d00001 diff --git a/doc/op-pushed-in-mr.png b/doc/op-pushed-in-mr.png new file mode 100644 index 0000000000000000000000000000000000000000..2550e592afe8a7245f2c3936af4e1a590f1f3874 GIT binary patch literal 55585 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 literal 0 HcmV?d00001 diff --git a/doc/op-referenced-in-issue.png b/doc/op-referenced-in-issue.png new file mode 100644 index 0000000000000000000000000000000000000000..0724dde7d345be465b79b9fcbb3c66249c17e24d GIT binary patch literal 57509 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 literal 0 HcmV?d00001 From e992ca8456c7059b78512e727a5cbe900157b812 Mon Sep 17 00:00:00 2001 From: btey Date: Thu, 25 Mar 2021 12:05:56 +0100 Subject: [PATCH 06/69] Updated Readme --- README.md | 118 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 7476bfe11226..6c66b43ffde7 100644 --- a/README.md +++ b/README.md @@ -17,54 +17,96 @@ OpenProject will **update WP status** in this events: * Merge Request (opened) - Status: In progress (currently ID=7) * Merge Request (merged) - Status: Developed (currently ID=8) +> **Note about the status.** If you want to change the ID of the status you can do this in this section of the [code](https://github.com/btey/openproject-gitlab-integration/blob/58279c79035539bdd127d14e2fd148c06d85a15a/lib/open_project/gitlab_integration/notification_handlers.rb#L108-L111). +> +> **Note about the references.** Whether or not to include the reference in certain places depends on the information that Gitlab sends through its webhook. If you include the reference in the title of an issue, the comments on the issue do not need to include the reference. The same will happen when you generate a Merge Request based on an Issue that already includes the reference; comments from that MR need not include the reference. + ## Example workflow A typical workflow on Gitlab side would be: 1. **Create Issue.** + + + > **Issue Opened:** Issue 6 New contact form - OP#18 for Scrum project has been opened by Administrator. 2. **Comment on issue.** -> **Commented in Issue:** Administrator commented this WP in Issue 6 New contact form - OP#18 on Scrum project: -> -> New comment on the issue with attachment. + + If the reference is included in the title, the comments will not need a reference. By default, all comments will use the title as a reference. + + + + > **Commented in Issue:** Administrator commented this WP in Issue 6 New contact form - OP#18 on Scrum project: + > + > New comment on the issue with attachment. + 3. **Create Merge Request.** -> **MR Opened:** Merge request 25 Draft: Resolve "New contact form - OP#18" for Scrum project has been opened by Administrator. -> -> **Status** changed from _Specified_ -> **to** _In progress_ + + + + > **MR Opened:** Merge request 25 Draft: Resolve "New contact form - OP#18" for Scrum project has been opened by Administrator. + > + > **Status** changed from _Specified_ + > **to** _In progress_ + 4. **Comment in Merge Request.** -> **Commented in MR:** Administrator commented this WP in Merge request 25 Draft: Resolve "New contact form - OP#18" on Scrum project: -> -> New comment on MR. -5. **Reference in other Issues (comments).** -> **Referenced in Issue:** Administrator referenced this WP in Issue 2 New backend pipeline on Scrum project: -> -> OP#18 New comment about... + + + + > **Commented in MR:** Administrator commented this WP in Merge request 25 Draft: Resolve "New contact form - OP#18" on Scrum project: + > + > New comment on MR. + +5. **Reference in other Issues or Merge Request (comments).** + + If the reference is NOT included in the title of the Issue/MR, the comments will need a reference. In OpenProject the comment will be saved as "referenced" in Issue/MR. + + + + > **Referenced in Issue:** Administrator referenced this WP in Issue 2 New backend pipeline on Scrum project: + > + > OP#18 New comment about... + 6. **New commit in Merge Request.** -> **Pushed in MR:** Administrator pushed fca3d6fb to Scrum project at 2021-03-08T08:01:57+00:00: -> -> Update readme.md OP#18 + + + + > **Pushed in MR:** Administrator pushed fca3d6fb to Scrum project at 2021-03-08T08:01:57+00:00: + > + > Update readme.md OP#18 + 7. **Comment in a new commit of the Merge Request.** -> **Referenced in Commit:** Administrator referenced this WP in a Commit Note 0bf0e3e9 on Scrum project: -> -> This change is for OP#18. + + + + > **Referenced in Commit:** Administrator referenced this WP in a Commit Note 0bf0e3e9 on Scrum project: + > + > This change is for OP#18. + 8. **Merge Request merged (generates up to 3 events).** -> **Pushed in MR:** Administrator pushed 1da09cb4 to Scrum project at 2021-03-05T14:57:37+00:00: -> -> Merge branch '5-new-contact-form-op-18' into 'master' -> -> Resolve "New contact form - OP#18" -> -> Closes #6 -> -> See merge request root/scrum!9 - -> **MR Merged:** Merge request 24 Resolve "New contact form - OP#18" for Scrum project has been merged by Administrator. -> -> **Status** changed from _In progress_ -> **to** _Developed_ - -> **Issue Closed:** Issue 6 New contact form - OP#18 for Scrum project has been closed by Administrator. + + + + > **Pushed in MR:** Administrator pushed 1da09cb4 to Scrum project at 2021-03-05T14:57:37+00:00: + > + > Merge branch '5-new-contact-form-op-18' into 'master' + > + > Resolve "New contact form - OP#18" + > + > Closes #6 + > + > See merge request root/scrum!9 + + + + > **MR Merged:** Merge request 24 Resolve "New contact form - OP#18" for Scrum project has been merged by Administrator. + > + > **Status** changed from _In progress_ + > **to** _Developed_ + + + + > **Issue Closed:** Issue 6 New contact form - OP#18 for Scrum project has been closed by Administrator. ## Configuration @@ -112,3 +154,7 @@ http://openproject-url.com/webhooks/gitlab?key=ae278268 2. Enable the required triggers: Push events, Comments, Issues events, Merge request events Now the integration is set up on both sides and you can use it. + +## How to report bugs or issues + +Any error, bug or issue can be reported by creating a new [issue](https://github.com/btey/openproject-gitlab-integration/issues/new). From 12d65f05b7f8b43859eadeefbd1ac5ee8b76839f Mon Sep 17 00:00:00 2001 From: btey Date: Thu, 25 Mar 2021 12:06:30 +0100 Subject: [PATCH 07/69] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c66b43ffde7..7d2126a1edde 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ A typical workflow on Gitlab side would be: -> **Issue Opened:** Issue 6 New contact form - OP#18 for Scrum project has been opened by Administrator. + > **Issue Opened:** Issue 6 New contact form - OP#18 for Scrum project has been opened by Administrator. 2. **Comment on issue.** From 06ca5d39c3b37ca57e5ad7320f2e40d413ae8b36 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Fri, 26 Nov 2021 14:30:01 +0100 Subject: [PATCH 08/69] Pre-release version with copyright --- app/models/gitlab_merge_request.rb | 101 +++++++ app/models/gitlab_pipeline.rb | 48 ++++ app/models/gitlab_user.rb | 40 +++ .../cron/clear_old_merge_requests_job.rb | 44 +++ config/locales/crowdin/ar.yml | 58 ++-- config/locales/crowdin/bg.yml | 58 ++-- config/locales/crowdin/ca.yml | 58 ++-- config/locales/crowdin/cs.yml | 58 ++-- config/locales/crowdin/da.yml | 58 ++-- config/locales/crowdin/de.yml | 58 ++-- config/locales/crowdin/el.yml | 58 ++-- config/locales/crowdin/es.yml | 58 ++-- config/locales/crowdin/fi.yml | 58 ++-- config/locales/crowdin/fil.yml | 58 ++-- config/locales/crowdin/fr.yml | 58 ++-- config/locales/crowdin/hr.yml | 58 ++-- config/locales/crowdin/hu.yml | 58 ++-- config/locales/crowdin/id.yml | 58 ++-- config/locales/crowdin/it.yml | 58 ++-- config/locales/crowdin/ja.yml | 58 ++-- config/locales/crowdin/js-ar.yml | 51 ++++ config/locales/crowdin/js-bg.yml | 51 ++++ config/locales/crowdin/js-ca.yml | 51 ++++ config/locales/crowdin/js-cs.yml | 51 ++++ config/locales/crowdin/js-da.yml | 51 ++++ config/locales/crowdin/js-de.yml | 51 ++++ config/locales/crowdin/js-el.yml | 51 ++++ config/locales/crowdin/js-es.yml | 51 ++++ config/locales/crowdin/js-fi.yml | 51 ++++ config/locales/crowdin/js-fil.yml | 51 ++++ config/locales/crowdin/js-fr.yml | 51 ++++ config/locales/crowdin/js-hr.yml | 51 ++++ config/locales/crowdin/js-hu.yml | 51 ++++ config/locales/crowdin/js-id.yml | 51 ++++ config/locales/crowdin/js-it.yml | 51 ++++ config/locales/crowdin/js-ja.yml | 51 ++++ config/locales/crowdin/js-ko.yml | 51 ++++ config/locales/crowdin/js-lt.yml | 51 ++++ config/locales/crowdin/js-nl.yml | 51 ++++ config/locales/crowdin/js-no.yml | 51 ++++ config/locales/crowdin/js-pl.yml | 51 ++++ config/locales/crowdin/js-pt.yml | 51 ++++ config/locales/crowdin/js-ro.yml | 51 ++++ config/locales/crowdin/js-ru.yml | 51 ++++ config/locales/crowdin/js-sk.yml | 51 ++++ config/locales/crowdin/js-sl.yml | 51 ++++ config/locales/crowdin/js-sv.yml | 51 ++++ config/locales/crowdin/js-tr.yml | 51 ++++ config/locales/crowdin/js-uk.yml | 51 ++++ config/locales/crowdin/js-vi.yml | 51 ++++ config/locales/crowdin/js-zh-CN.yml | 51 ++++ config/locales/crowdin/js-zh-TW.yml | 51 ++++ config/locales/crowdin/ko.yml | 58 ++-- config/locales/crowdin/lt.yml | 58 ++-- config/locales/crowdin/nl.yml | 58 ++-- config/locales/crowdin/no.yml | 58 ++-- config/locales/crowdin/pl.yml | 58 ++-- config/locales/crowdin/pt.yml | 58 ++-- config/locales/crowdin/ro.yml | 58 ++-- config/locales/crowdin/ru.yml | 58 ++-- config/locales/crowdin/sk.yml | 58 ++-- config/locales/crowdin/sl.yml | 58 ++-- config/locales/crowdin/sv.yml | 58 ++-- config/locales/crowdin/tr.yml | 58 ++-- config/locales/crowdin/uk.yml | 58 ++-- config/locales/crowdin/vi.yml | 58 ++-- config/locales/crowdin/zh-CN.yml | 58 ++-- config/locales/crowdin/zh-TW.yml | 58 ++-- config/locales/de.yml | 25 +- config/locales/en.yml | 23 +- config/locales/js-en.yml | 52 ++++ ...0211015110000_gitlab_integration_models.rb | 88 ++++++ .../git-actions-menu.component.ts | 106 +++++++ .../git-actions-menu.directive.ts | 60 ++++ .../git-actions-menu.template.html | 22 ++ .../styles/git-actions-menu.sass | 90 ++++++ .../module/git-actions/git-actions.service.ts | 65 +++++ .../module/gitlab-tab/gitlab-tab.component.ts | 46 +++ .../gitlab-tab/gitlab-tab.template.html | 2 + .../gitlab-merge-request-resource.ts | 43 +++ .../resources/gitlab-pipelines-resource.ts | 43 +++ .../hal/resources/gitlab-user-resource.ts | 43 +++ frontend/module/main.ts | 82 ++++++ .../merge-request.component.html | 69 +++++ .../merge-request.component.sass | 123 ++++++++ .../merge-request/merge-request.component.ts | 117 ++++++++ .../merge-request/mr-pipeline.component.sass | 90 ++++++ .../module/tab-header/styles/tab-header.sass | 47 +++ .../module/tab-header/tab-header.component.ts | 54 ++++ .../tab-header/tab-header.template.html | 21 ++ frontend/module/tab-mrs/tab-mrs.component.ts | 70 +++++ frontend/module/tab-mrs/tab-mrs.template.html | 5 + .../module/tab-mrs/wp-gitlab-mrs.service.ts | 52 ++++ frontend/module/typings.d.ts | 77 +++++ ...ab_merge_request_collection_representer.rb | 40 +++ .../gitlab_merge_request_representer.rb | 108 +++++++ ...tlab_merge_requests_by_work_package_api.rb | 53 ++++ .../gitlab_pipeline_representer.rb | 59 ++++ .../gitlab_user_representer.rb | 52 ++++ lib/open_project/gitlab_integration.rb | 5 +- lib/open_project/gitlab_integration/engine.rb | 44 ++- .../gitlab_integration/hook_handler.rb | 19 +- .../notification_handler/helper.rb | 151 ++++++++++ .../notification_handler/issue_hook.rb | 71 +++++ .../merge_request_hook.rb | 109 +++++++ .../notification_handler/note_hook.rb | 146 ++++++++++ .../notification_handler/pipeline_hook.rb | 65 +++++ .../notification_handler/push_hook.rb | 67 +++++ .../notification_handlers.rb | 272 +++--------------- .../patches/api/work_package_representer.rb | 56 ++++ .../gitlab_integration/services.rb | 32 +++ .../services/upsert_gitlab_user.rb | 60 ++++ .../services/upsert_merge_request.rb | 88 ++++++ .../services/upsert_pipeline.rb | 64 +++++ lib/openproject-gitlab_integration.rb | 5 +- openproject-gitlab_integration.gemspec | 7 +- 116 files changed, 5937 insertions(+), 872 deletions(-) create mode 100644 app/models/gitlab_merge_request.rb create mode 100644 app/models/gitlab_pipeline.rb create mode 100644 app/models/gitlab_user.rb create mode 100644 app/workers/cron/clear_old_merge_requests_job.rb create mode 100644 config/locales/crowdin/js-ar.yml create mode 100644 config/locales/crowdin/js-bg.yml create mode 100644 config/locales/crowdin/js-ca.yml create mode 100644 config/locales/crowdin/js-cs.yml create mode 100644 config/locales/crowdin/js-da.yml create mode 100644 config/locales/crowdin/js-de.yml create mode 100644 config/locales/crowdin/js-el.yml create mode 100644 config/locales/crowdin/js-es.yml create mode 100644 config/locales/crowdin/js-fi.yml create mode 100644 config/locales/crowdin/js-fil.yml create mode 100644 config/locales/crowdin/js-fr.yml create mode 100644 config/locales/crowdin/js-hr.yml create mode 100644 config/locales/crowdin/js-hu.yml create mode 100644 config/locales/crowdin/js-id.yml create mode 100644 config/locales/crowdin/js-it.yml create mode 100644 config/locales/crowdin/js-ja.yml create mode 100644 config/locales/crowdin/js-ko.yml create mode 100644 config/locales/crowdin/js-lt.yml create mode 100644 config/locales/crowdin/js-nl.yml create mode 100644 config/locales/crowdin/js-no.yml create mode 100644 config/locales/crowdin/js-pl.yml create mode 100644 config/locales/crowdin/js-pt.yml create mode 100644 config/locales/crowdin/js-ro.yml create mode 100644 config/locales/crowdin/js-ru.yml create mode 100644 config/locales/crowdin/js-sk.yml create mode 100644 config/locales/crowdin/js-sl.yml create mode 100644 config/locales/crowdin/js-sv.yml create mode 100644 config/locales/crowdin/js-tr.yml create mode 100644 config/locales/crowdin/js-uk.yml create mode 100644 config/locales/crowdin/js-vi.yml create mode 100644 config/locales/crowdin/js-zh-CN.yml create mode 100644 config/locales/crowdin/js-zh-TW.yml create mode 100644 config/locales/js-en.yml create mode 100644 db/migrate/20211015110000_gitlab_integration_models.rb create mode 100644 frontend/module/git-actions-menu/git-actions-menu.component.ts create mode 100644 frontend/module/git-actions-menu/git-actions-menu.directive.ts create mode 100644 frontend/module/git-actions-menu/git-actions-menu.template.html create mode 100644 frontend/module/git-actions-menu/styles/git-actions-menu.sass create mode 100644 frontend/module/git-actions/git-actions.service.ts create mode 100644 frontend/module/gitlab-tab/gitlab-tab.component.ts create mode 100644 frontend/module/gitlab-tab/gitlab-tab.template.html create mode 100644 frontend/module/hal/resources/gitlab-merge-request-resource.ts create mode 100644 frontend/module/hal/resources/gitlab-pipelines-resource.ts create mode 100644 frontend/module/hal/resources/gitlab-user-resource.ts create mode 100644 frontend/module/main.ts create mode 100644 frontend/module/merge-request/merge-request.component.html create mode 100644 frontend/module/merge-request/merge-request.component.sass create mode 100644 frontend/module/merge-request/merge-request.component.ts create mode 100644 frontend/module/merge-request/mr-pipeline.component.sass create mode 100644 frontend/module/tab-header/styles/tab-header.sass create mode 100644 frontend/module/tab-header/tab-header.component.ts create mode 100644 frontend/module/tab-header/tab-header.template.html create mode 100644 frontend/module/tab-mrs/tab-mrs.component.ts create mode 100644 frontend/module/tab-mrs/tab-mrs.template.html create mode 100644 frontend/module/tab-mrs/wp-gitlab-mrs.service.ts create mode 100644 frontend/module/typings.d.ts create mode 100644 lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb create mode 100644 lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb create mode 100644 lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb create mode 100644 lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb create mode 100644 lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb create mode 100644 lib/open_project/gitlab_integration/notification_handler/helper.rb create mode 100644 lib/open_project/gitlab_integration/notification_handler/issue_hook.rb create mode 100644 lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb create mode 100644 lib/open_project/gitlab_integration/notification_handler/note_hook.rb create mode 100644 lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb create mode 100644 lib/open_project/gitlab_integration/notification_handler/push_hook.rb create mode 100644 lib/open_project/gitlab_integration/patches/api/work_package_representer.rb create mode 100644 lib/open_project/gitlab_integration/services.rb create mode 100644 lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb create mode 100644 lib/open_project/gitlab_integration/services/upsert_merge_request.rb create mode 100644 lib/open_project/gitlab_integration/services/upsert_pipeline.rb diff --git a/app/models/gitlab_merge_request.rb b/app/models/gitlab_merge_request.rb new file mode 100644 index 000000000000..6218467d5e39 --- /dev/null +++ b/app/models/gitlab_merge_request.rb @@ -0,0 +1,101 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +class GitlabMergeRequest < ApplicationRecord + LABEL_KEYS = %w[color title].freeze + + has_and_belongs_to_many :work_packages + has_many :gitlab_pipelines, dependent: :destroy + has_many :github_check_runs, dependent: :destroy + belongs_to :gitlab_user, optional: true + belongs_to :merged_by, optional: true, class_name: 'GitlabUser' + + enum state: { + opened: 'opened', + merged: 'merged', + closed: 'closed' + } + + validates_presence_of :gitlab_html_url, + :number, + :repository, + :state, + :title, + :gitlab_updated_at + validates_presence_of :body, + unless: :partial? + validate :validate_labels_schema + + scope :without_work_package, -> { left_outer_joins(:work_packages).where(work_packages: { id: nil }) } + + def self.find_by_gitlab_identifiers(id: nil, url: nil, initialize: false) + raise ArgumentError, "needs an id or an url" if id.nil? && url.blank? + + found = where(gitlab_id: id).or(where(gitlab_html_url: url)).take + + if found + found + elsif initialize + new(gitlab_id: id, gitlab_html_url: url) + end + end + + ## + # When a MR lives long enough and receives many pushes, the same pipeline CI can be run multiple times. + # This method only returns the latest. + + def latest_pipelines + with_logging do + gitlab_pipelines.select("DISTINCT ON (gitlab_pipelines.project_id, gitlab_pipelines.name) *") + .order(project_id: :asc, name: :asc, started_at: :desc) + end + end + + def partial? + [body].all?(&:nil?) + end + + private + + def validate_labels_schema + return if labels.nil? + return if labels.all? { |label| label.keys.sort == LABEL_KEYS } + + errors.add(:labels, 'invalid schema') + end + + def with_logging + yield if block_given? + rescue StandardError => e + Rails.logger.error "Error at latest_pipeline: #{e} #{e.message}" + raise e + end +end diff --git a/app/models/gitlab_pipeline.rb b/app/models/gitlab_pipeline.rb new file mode 100644 index 000000000000..338899c757c7 --- /dev/null +++ b/app/models/gitlab_pipeline.rb @@ -0,0 +1,48 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +class GitlabPipeline < ApplicationRecord + belongs_to :gitlab_merge_request, touch: true + + # TODO: confirm with the gitlab documentation what are the different statuses. + enum status: { + completed: 'completed', + in_progress: 'in_progress', + success: 'success', + queued: 'queued' + } + + validates_presence_of :gitlab_user_avatar_url, + :gitlab_html_url, + :gitlab_id, + :status, + :name +end diff --git a/app/models/gitlab_user.rb b/app/models/gitlab_user.rb new file mode 100644 index 000000000000..31d4212bcffb --- /dev/null +++ b/app/models/gitlab_user.rb @@ -0,0 +1,40 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +class GitlabUser < ApplicationRecord + has_many :gitlab_merge_requests + + validates_presence_of :gitlab_id, + :gitlab_name, + :gitlab_username, + :gitlab_email, + :gitlab_avatar_url +end diff --git a/app/workers/cron/clear_old_merge_requests_job.rb b/app/workers/cron/clear_old_merge_requests_job.rb new file mode 100644 index 000000000000..80f85238ea6a --- /dev/null +++ b/app/workers/cron/clear_old_merge_requests_job.rb @@ -0,0 +1,44 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module Cron + class ClearOldMergeRequestsJob < CronJob + priority_number :low + + # runs at 1:25 nightly + self.cron_expression = '25 1 * * *' + + def perform + GitlabMergeRequest.without_work_package + .find_each(&:destroy!) + end + end +end diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index 44f4ee2918e9..d89f971c8b4a 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ ar: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ ar: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ ar: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index b4fdf7f5deed..02fd62659447 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ bg: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ bg: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ bg: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index 140b08144c43..bf38da46bea1 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ ca: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ ca: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ ca: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index 6d8f297f8822..8cddda63ec4a 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ cs: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ cs: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ cs: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index 0545f2c6e12f..b92d366080fd 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ da: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ da: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ da: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index e36fbc3514b4..fdec7b9cc12e 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ de: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ de: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ de: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index c0269806d7c2..32ece46687e8 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ el: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ el: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ el: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index 29dd7d62c964..b241daab87f3 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ es: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ es: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ es: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index 11dce857a10e..96db43ec7d08 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ fi: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ fi: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ fi: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index cc93fed5b82d..8f3c45b2f193 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ fil: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ fil: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ fil: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 5efbb9c5c2d6..64d8c8a87f79 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ fr: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ fr: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ fr: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index d7fb39a13565..c242ff118e52 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ hr: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ hr: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ hr: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index f73bc81ea158..cd0a5322b376 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ hu: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ hu: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ hu: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index 1caca2337954..fee8c289aba1 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ id: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ id: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ id: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 4be3b75a45c8..1958ba0a681e 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ it: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ it: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ it: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index c8145a0f5950..d1d0067e71ab 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ ja: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ ja: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ ja: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/js-ar.yml b/config/locales/crowdin/js-ar.yml new file mode 100644 index 000000000000..80acef0cbbc9 --- /dev/null +++ b/config/locales/crowdin/js-ar.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +ar: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-bg.yml b/config/locales/crowdin/js-bg.yml new file mode 100644 index 000000000000..97ab6ef8f384 --- /dev/null +++ b/config/locales/crowdin/js-bg.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +bg: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-ca.yml b/config/locales/crowdin/js-ca.yml new file mode 100644 index 000000000000..0b6d059dfc5b --- /dev/null +++ b/config/locales/crowdin/js-ca.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +ca: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-cs.yml b/config/locales/crowdin/js-cs.yml new file mode 100644 index 000000000000..650fb0fb19f6 --- /dev/null +++ b/config/locales/crowdin/js-cs.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +cs: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-da.yml b/config/locales/crowdin/js-da.yml new file mode 100644 index 000000000000..5a0dfb4166cc --- /dev/null +++ b/config/locales/crowdin/js-da.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +da: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-de.yml b/config/locales/crowdin/js-de.yml new file mode 100644 index 000000000000..7f977cf9e04a --- /dev/null +++ b/config/locales/crowdin/js-de.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +de: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-el.yml b/config/locales/crowdin/js-el.yml new file mode 100644 index 000000000000..7f137d133e56 --- /dev/null +++ b/config/locales/crowdin/js-el.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +el: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-es.yml b/config/locales/crowdin/js-es.yml new file mode 100644 index 000000000000..7197c175e9dd --- /dev/null +++ b/config/locales/crowdin/js-es.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +es: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-fi.yml b/config/locales/crowdin/js-fi.yml new file mode 100644 index 000000000000..bbeb52128acf --- /dev/null +++ b/config/locales/crowdin/js-fi.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +fi: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-fil.yml b/config/locales/crowdin/js-fil.yml new file mode 100644 index 000000000000..cfc2447a0536 --- /dev/null +++ b/config/locales/crowdin/js-fil.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +fil: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-fr.yml b/config/locales/crowdin/js-fr.yml new file mode 100644 index 000000000000..92c71735c982 --- /dev/null +++ b/config/locales/crowdin/js-fr.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +fr: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-hr.yml b/config/locales/crowdin/js-hr.yml new file mode 100644 index 000000000000..68da7f355d4e --- /dev/null +++ b/config/locales/crowdin/js-hr.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +hr: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-hu.yml b/config/locales/crowdin/js-hu.yml new file mode 100644 index 000000000000..42d437c163c5 --- /dev/null +++ b/config/locales/crowdin/js-hu.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +hu: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-id.yml b/config/locales/crowdin/js-id.yml new file mode 100644 index 000000000000..b8b027240f00 --- /dev/null +++ b/config/locales/crowdin/js-id.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +id: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-it.yml b/config/locales/crowdin/js-it.yml new file mode 100644 index 000000000000..6b3fc0e79125 --- /dev/null +++ b/config/locales/crowdin/js-it.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +it: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-ja.yml b/config/locales/crowdin/js-ja.yml new file mode 100644 index 000000000000..ada7bc1dba95 --- /dev/null +++ b/config/locales/crowdin/js-ja.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +ja: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-ko.yml b/config/locales/crowdin/js-ko.yml new file mode 100644 index 000000000000..bf9ca57c6975 --- /dev/null +++ b/config/locales/crowdin/js-ko.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +ko: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-lt.yml b/config/locales/crowdin/js-lt.yml new file mode 100644 index 000000000000..730001aae8df --- /dev/null +++ b/config/locales/crowdin/js-lt.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +lt: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-nl.yml b/config/locales/crowdin/js-nl.yml new file mode 100644 index 000000000000..f92a3b9881f0 --- /dev/null +++ b/config/locales/crowdin/js-nl.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +nl: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-no.yml b/config/locales/crowdin/js-no.yml new file mode 100644 index 000000000000..d59db3b7e2a9 --- /dev/null +++ b/config/locales/crowdin/js-no.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +"no": + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-pl.yml b/config/locales/crowdin/js-pl.yml new file mode 100644 index 000000000000..e3122e06816c --- /dev/null +++ b/config/locales/crowdin/js-pl.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +pl: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-pt.yml b/config/locales/crowdin/js-pt.yml new file mode 100644 index 000000000000..58a97e0cb3a3 --- /dev/null +++ b/config/locales/crowdin/js-pt.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +pt: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-ro.yml b/config/locales/crowdin/js-ro.yml new file mode 100644 index 000000000000..e594199bc251 --- /dev/null +++ b/config/locales/crowdin/js-ro.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +ro: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml new file mode 100644 index 000000000000..44566acea887 --- /dev/null +++ b/config/locales/crowdin/js-ru.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +ru: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-sk.yml b/config/locales/crowdin/js-sk.yml new file mode 100644 index 000000000000..88bb174b562c --- /dev/null +++ b/config/locales/crowdin/js-sk.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +sk: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-sl.yml b/config/locales/crowdin/js-sl.yml new file mode 100644 index 000000000000..85f50aedc827 --- /dev/null +++ b/config/locales/crowdin/js-sl.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +sl: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-sv.yml b/config/locales/crowdin/js-sv.yml new file mode 100644 index 000000000000..25946481aa20 --- /dev/null +++ b/config/locales/crowdin/js-sv.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +sv: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-tr.yml b/config/locales/crowdin/js-tr.yml new file mode 100644 index 000000000000..4ce717f2e344 --- /dev/null +++ b/config/locales/crowdin/js-tr.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +tr: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-uk.yml b/config/locales/crowdin/js-uk.yml new file mode 100644 index 000000000000..29c081cf91cd --- /dev/null +++ b/config/locales/crowdin/js-uk.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +uk: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-vi.yml b/config/locales/crowdin/js-vi.yml new file mode 100644 index 000000000000..84e2c7fa5a1e --- /dev/null +++ b/config/locales/crowdin/js-vi.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +vi: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-zh-CN.yml b/config/locales/crowdin/js-zh-CN.yml new file mode 100644 index 000000000000..fdcec3786265 --- /dev/null +++ b/config/locales/crowdin/js-zh-CN.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +zh-CN: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/js-zh-TW.yml b/config/locales/crowdin/js-zh-TW.yml new file mode 100644 index 000000000000..6cdf89f89a98 --- /dev/null +++ b/config/locales/crowdin/js-zh-TW.yml @@ -0,0 +1,51 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +zh-TW: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. + gitlab_actions: Actions diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index e1d8d4140228..ec4ee1f661e5 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ ko: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ ko: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ ko: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index 0758e84742b4..ba52cf949c9e 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ lt: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ lt: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ lt: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index 16fa3ed5f4c4..bb25bfdaa700 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ nl: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ nl: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ nl: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index 29178460bb6d..7f32b8b23e91 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ "no": + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index e47f53730eb5..53c2816779fe 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ pl: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ pl: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ pl: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/pt.yml b/config/locales/crowdin/pt.yml index 78016bcdd5e6..211a21525f41 100644 --- a/config/locales/crowdin/pt.yml +++ b/config/locales/crowdin/pt.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ pt: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ pt: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ pt: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index c51762ddcfc2..d26476323cd4 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ ro: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ ro: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ ro: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 79d7107e480c..11730a838449 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ ru: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ ru: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ ru: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index ee2d1b002f49..fd444881cddc 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ sk: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ sk: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ sk: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index d00171394732..bc460be9ed94 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ sl: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ sl: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ sl: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index f8bbf7383c2c..10baf1b2fca5 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ sv: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ sv: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ sv: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index ad4a49c64c64..12d07288b188 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ tr: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ tr: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ tr: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index 7b6f1e7b1cd7..8e2ae9a931ee 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ uk: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ uk: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ uk: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index 8c4d8e48ee46..a8b763826baf 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ vi: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ vi: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ vi: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml index 69a9026c4704..14265438a7d0 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ zh-CN: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ zh-CN: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ zh-CN: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index 1f59e50c02a2..c3db200526fb 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -1,25 +1,34 @@ #-- copyright -#OpenProject is an open source project management software. -#Copyright (C) 2012-2020 the OpenProject GmbH -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License version 3. -#OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -#Copyright (C) 2006-2017 Jean-Philippe Lang -#Copyright (C) 2010-2013 the ChiliProject Team -#This program is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public License -#as published by the Free Software Foundation; either version 2 -#of the License, or (at your option) any later version. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -#You should have received a copy of the GNU General Public License -#along with this program; if not, write to the Free Software -#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#See docs/COPYRIGHT.rdoc for more details. +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. #++ zh-TW: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -30,6 +39,9 @@ zh-TW: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -66,8 +78,16 @@ zh-TW: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/de.yml b/config/locales/de.yml index c337a83a8668..ef813ecb454d 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,7 +27,10 @@ # See docs/COPYRIGHT.rdoc for more details. #++ -en: +de: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" + gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -37,6 +41,9 @@ en: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -44,12 +51,12 @@ en: %{commit_note} note_mr_referenced_comment: > **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in - Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} note_mr_commented_comment: > **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in - Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} note_issue_referenced_comment: > @@ -73,8 +80,16 @@ en: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/en.yml b/config/locales/en.yml index c337a83a8668..f31ceded9587 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -27,6 +28,9 @@ #++ en: + project_module_gitlab: "Gitlab" + permission_show_gitlab_content: "Show Gitlab content" + gitlab_integration: merge_request_opened_comment: > **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) @@ -37,6 +41,9 @@ en: merge_request_merged_comment: > **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been merged by [%{gitlab_user}](%{gitlab_user_url}). + merge_request_reopened_comment: > + **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): @@ -44,12 +51,12 @@ en: %{commit_note} note_mr_referenced_comment: > **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in - Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} note_mr_commented_comment: > **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in - Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} note_issue_referenced_comment: > @@ -73,8 +80,16 @@ en: issue_closed_referenced_comment: > **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been closed by [%{gitlab_user}](%{gitlab_user_url}). + issue_reopened_referenced_comment: > + **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) + has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} + push_multiple_commits_comment: > + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) + to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml new file mode 100644 index 000000000000..5e3d38f67caf --- /dev/null +++ b/config/locales/js-en.yml @@ -0,0 +1,52 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +en: + js: + gitlab_integration: + work_packages: + tab_name: "GitLab" + tab_header: + title: "Merge requests" + create_mr: + label: Create MR + description: Create a Merge Request + copy_menu: + label: Git snippets + description: Copy git snippets to clipboard + git_actions: + branch_name: Branch name + commit_message: Commit message + cmd: Create branch with empty commit + title: Quick snippets for Git + copy_success: ✅ Copied! + copy_error: ❌ Copy failed! + tab_mrs: + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines diff --git a/db/migrate/20211015110000_gitlab_integration_models.rb b/db/migrate/20211015110000_gitlab_integration_models.rb new file mode 100644 index 000000000000..bf1f58ce8e6f --- /dev/null +++ b/db/migrate/20211015110000_gitlab_integration_models.rb @@ -0,0 +1,88 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +class GitlabIntegrationModels < ActiveRecord::Migration[6.1] + # rubocop:disable Metrics/AbcSize + def change + create_table :gitlab_merge_requests do |t| + t.references :gitlab_user + t.references :merged_by + + t.bigint :gitlab_id, unique: true + t.integer :number, null: false + t.string :gitlab_html_url, null: false, unique: true + t.string :state, null: false + t.string :repository, null: false + t.datetime :gitlab_updated_at + t.string :title + t.text :body + t.boolean :draft + t.boolean :merged + t.datetime :merged_at + t.json :labels # [{name, color}] + t.timestamps + end + + create_join_table :gitlab_merge_requests, :work_packages do |t| + t.index :gitlab_merge_request_id, name: 'gitlab_mr_wp_mr_id' + t.index %i[gitlab_merge_request_id work_package_id], + unique: true, + name: "unique_index_gl_mrs_wps_on_gl_mr_id_and_wp_id" + end + + + create_table :gitlab_users do |t| + t.bigint :gitlab_id, null: false, unique: true + t.string :gitlab_name, null: false + t.string :gitlab_username, null: false + t.string :gitlab_email, null: false + t.string :gitlab_avatar_url, null: false + + t.timestamps + end + + + create_table :gitlab_pipelines do |t| + t.references :gitlab_merge_request, null: false + + t.bigint :gitlab_id, null: false, unique: true + t.string :gitlab_html_url, null: false + t.bigint :project_id, null: false + t.string :gitlab_user_avatar_url, null: false + t.string :status, null: false + t.string :name, null: false + t.string :details_url + t.json :ci_details + t.datetime :started_at + t.datetime :completed_at + + t.timestamps + end + end + # rubocop:enable Metrics/AbcSize +end diff --git a/frontend/module/git-actions-menu/git-actions-menu.component.ts b/frontend/module/git-actions-menu/git-actions-menu.component.ts new file mode 100644 index 000000000000..c0efeb16045b --- /dev/null +++ b/frontend/module/git-actions-menu/git-actions-menu.component.ts @@ -0,0 +1,106 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import copy from 'copy-text-to-clipboard'; +import { Component, Inject, Input } from '@angular/core'; +import { WorkPackageResource } from 'core-app/modules/hal/resources/work-package-resource'; +import { I18nService } from 'core-app/modules/common/i18n/i18n.service'; +import { GitActionsService } from '../git-actions/git-actions.service'; +import { OPContextMenuComponent } from 'core-app/components/op-context-menu/op-context-menu.component'; +import { + OpContextMenuLocalsMap, + OpContextMenuLocalsToken +} from 'core-app/components/op-context-menu/op-context-menu.types'; +import { ISnippet} from "core-app/modules/plugins/linked/openproject-gitlab_integration/typings"; + + +@Component({ + selector: 'op-git-actions-menu', + templateUrl: './git-actions-menu.template.html', + styleUrls: [ + './styles/git-actions-menu.sass' + ] +}) +export class GitActionsMenuComponent extends OPContextMenuComponent { + @Input() public workPackage:WorkPackageResource; + + public text = { + title: this.I18n.t('js.gitlab_integration.tab_header.git_actions.title'), + copyButtonHelpText: this.I18n.t('js.gitlab_integration.tab_header.git_actions.copy_button_help'), + copyResult: { + success: this.I18n.t('js.gitlab_integration.tab_header.git_actions.copy_success'), + error: this.I18n.t('js.gitlab_integration.tab_header.git_actions.copy_error') + } + }; + + public lastCopyResult:string = this.text.copyResult.success; + public showCopyResult:boolean = false; + public copiedSnippetId:string = ''; + + public snippets:ISnippet[] = [ + { + id: 'branch', + name: this.I18n.t('js.gitlab_integration.tab_header.git_actions.branch_name'), + textToCopy: () => this.gitActions.branchName(this.workPackage) + }, + { + id: 'message', + name: this.I18n.t('js.gitlab_integration.tab_header.git_actions.commit_message'), + textToCopy: () => this.gitActions.commitMessage(this.workPackage) + }, + { + id: 'command', + name: this.I18n.t('js.gitlab_integration.tab_header.git_actions.cmd'), + textToCopy: () => this.gitActions.gitCommand(this.workPackage) + }, + ]; + + constructor(@Inject(OpContextMenuLocalsToken) + public locals:OpContextMenuLocalsMap, + readonly I18n:I18nService, + readonly gitActions:GitActionsService) { + super(locals); + this.workPackage = this.locals.workPackage; + } + + public onCopyButtonClick(snippet:ISnippet):void { + const success = copy(snippet.textToCopy()); + + if (success) { + this.lastCopyResult = this.text.copyResult.success; + } else { + this.lastCopyResult = this.text.copyResult.error; + } + this.copiedSnippetId = snippet.id; + this.showCopyResult = true; + window.setTimeout(() => { + this.showCopyResult = false; + }, 2000); + } +} diff --git a/frontend/module/git-actions-menu/git-actions-menu.directive.ts b/frontend/module/git-actions-menu/git-actions-menu.directive.ts new file mode 100644 index 000000000000..9275d0f38b76 --- /dev/null +++ b/frontend/module/git-actions-menu/git-actions-menu.directive.ts @@ -0,0 +1,60 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import {OpContextMenuItem} from 'core-components/op-context-menu/op-context-menu.types'; +import {OPContextMenuService} from 'core-components/op-context-menu/op-context-menu.service'; +import {Directive, ElementRef, Input} from '@angular/core'; +import {OpContextMenuTrigger} from 'core-components/op-context-menu/handlers/op-context-menu-trigger.directive'; +import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; +import {GitActionsMenuComponent} from './git-actions-menu.component'; + +@Directive({ + selector: '[gitActionsCopyDropdown]' +}) +export class GitActionsMenuDirective extends OpContextMenuTrigger { + @Input('gitActionsCopyDropdown-workPackage') public workPackage:WorkPackageResource; + + constructor(readonly elementRef:ElementRef, + readonly opContextMenu:OPContextMenuService) { + super(elementRef, opContextMenu); + } + + protected open(evt:JQuery.TriggeredEvent) { + this.opContextMenu.show(this, evt, GitActionsMenuComponent); + } + + public get locals():{ showAnchorRight?:boolean, contextMenuId?:string, items:OpContextMenuItem[], workPackage:WorkPackageResource } { + return { + workPackage: this.workPackage, + contextMenuId: 'gitlab-integration-git-actions-menu', + items: [] + }; + } +} + diff --git a/frontend/module/git-actions-menu/git-actions-menu.template.html b/frontend/module/git-actions-menu/git-actions-menu.template.html new file mode 100644 index 000000000000..0c2421885537 --- /dev/null +++ b/frontend/module/git-actions-menu/git-actions-menu.template.html @@ -0,0 +1,22 @@ + + + diff --git a/frontend/module/git-actions-menu/styles/git-actions-menu.sass b/frontend/module/git-actions-menu/styles/git-actions-menu.sass new file mode 100644 index 000000000000..4ad868111408 --- /dev/null +++ b/frontend/module/git-actions-menu/styles/git-actions-menu.sass @@ -0,0 +1,90 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ +.git-actions-menu + background-color: var(--body-background) + border: var(--content-default-border-width) solid var(--content-default-border-color) + padding: 1rem 1rem 2rem 1rem + min-width: 25rem + box-shadow: .1em .1em .4em rgba(0,0,0,0.1) + + .copy-wrapper + width: 100% + position: relative + margin-bottom: 1rem + + .copy-content + width: calc(100% - 3em) + // the min-height should be the size of the copy-icon, which is the sum of: + // 2 * button padding (0.65em) + // font-size of the icon (0.9em) + // 1px where I don't know where it comes from + min-height: calc(2 * 0.65em + 0.9em + 1px) + border-radius: 2px 0 0 2px + padding: 0.65em + color: var(--gray-dark) + white-space: pre + resize: none + font-size: 0.9rem + display: inline-block + + .copy-button + margin: 0 + border: 1px solid #ccc + border-radius: 0 2px 2px 0 + vertical-align: top + left: -1px + position: relative + font-size: 0.9rem + + &:hover + border-color: #999 + + .copy-result-message + background-color: var(--main-menu-bg-color) + display: inline-block + padding: 0.5em + border-radius: 5px + color: var(--main-menu-font-color) + position: absolute + right: 0 + top: calc(2 * 0.65em + 0.9em + 1px + 9px) + box-shadow: 1px 1px 4px var(--gray-dark) + z-index: 1 + + &:before + content: "" + border-bottom: 0.6em solid var(--main-menu-bg-color) + height: 0 + width: 0 + position: absolute + top: -9px + right: 10px + border-left: 0.3em solid transparent + border-right: 0.3em solid transparent + diff --git a/frontend/module/git-actions/git-actions.service.ts b/frontend/module/git-actions/git-actions.service.ts new file mode 100644 index 000000000000..fec13a499187 --- /dev/null +++ b/frontend/module/git-actions/git-actions.service.ts @@ -0,0 +1,65 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + + +import {Injectable} from '@angular/core'; +import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; +import formatter from 'tickety-tick-formatter'; + +// probably not providable in root when we want to cache the formatter and set custom templates +@Injectable({ + providedIn: 'root', +}) +export class GitActionsService { + private formatter = formatter(); + + public branchName(workPackage:WorkPackageResource):string { + return(this.formatter.branch(this.formattingInput(workPackage))); + } + + public commitMessage(workPackage:WorkPackageResource):string { + return(this.formatter.commit(this.formattingInput(workPackage))); + } + + public gitCommand(workPackage:WorkPackageResource):string { + return(this.formatter.command(this.formattingInput(workPackage))); + } + + private formattingInput(workPackage: WorkPackageResource) { + const type = workPackage.type.name || ''; + const id = workPackage.id || ''; + const title = workPackage.subject; + const url = window.location.origin + workPackage.pathHelper.workPackagePath(id); + const description = ''; + + return({ + id, type, title, url, description + }); + } +} \ No newline at end of file diff --git a/frontend/module/gitlab-tab/gitlab-tab.component.ts b/frontend/module/gitlab-tab/gitlab-tab.component.ts new file mode 100644 index 000000000000..f1abea8de1f2 --- /dev/null +++ b/frontend/module/gitlab-tab/gitlab-tab.component.ts @@ -0,0 +1,46 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import {Component, Input} from '@angular/core'; +import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; +import {PathHelperService} from 'core-app/modules/common/path-helper/path-helper.service'; +import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; +import { TabComponent } from 'core-app/components/wp-tabs/components/wp-tab-wrapper/tab'; + +@Component({ + selector: 'gitlab-tab', + templateUrl: './gitlab-tab.template.html' +}) +export class GitlabTabComponent implements TabComponent { + @Input() public workPackage:WorkPackageResource; + + constructor(readonly PathHelper:PathHelperService, + readonly I18n:I18nService) { + } +} diff --git a/frontend/module/gitlab-tab/gitlab-tab.template.html b/frontend/module/gitlab-tab/gitlab-tab.template.html new file mode 100644 index 000000000000..7bbfd3bd4bc5 --- /dev/null +++ b/frontend/module/gitlab-tab/gitlab-tab.template.html @@ -0,0 +1,2 @@ + + diff --git a/frontend/module/hal/resources/gitlab-merge-request-resource.ts b/frontend/module/hal/resources/gitlab-merge-request-resource.ts new file mode 100644 index 000000000000..391d76deae6a --- /dev/null +++ b/frontend/module/hal/resources/gitlab-merge-request-resource.ts @@ -0,0 +1,43 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; + +export class GitlabMergeRequestResource extends HalResource { + public get state() { + return this.states.projects.get(this.id!) as any; + } + + /** + * Exclude the schema _link from the linkable Resources. + */ + public $linkableKeys():string[] { + return _.without(super.$linkableKeys(), 'schema'); + } +} diff --git a/frontend/module/hal/resources/gitlab-pipelines-resource.ts b/frontend/module/hal/resources/gitlab-pipelines-resource.ts new file mode 100644 index 000000000000..26492f5b6a8b --- /dev/null +++ b/frontend/module/hal/resources/gitlab-pipelines-resource.ts @@ -0,0 +1,43 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; + +export class GitlabPipelineResource extends HalResource { + public get state() { + return this.states.projects.get(this.id!) as any; + } + + /** + * Exclude the schema _link from the linkable Resources. + */ + public $linkableKeys():string[] { + return _.without(super.$linkableKeys(), 'schema'); + } +} diff --git a/frontend/module/hal/resources/gitlab-user-resource.ts b/frontend/module/hal/resources/gitlab-user-resource.ts new file mode 100644 index 000000000000..bbcca7937ccd --- /dev/null +++ b/frontend/module/hal/resources/gitlab-user-resource.ts @@ -0,0 +1,43 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; + +export class GitlabUserResource extends HalResource { + public get state() { + return this.states.projects.get(this.id!) as any; + } + + /** + * Exclude the schema _link from the linkable Resources. + */ + public $linkableKeys():string[] { + return _.without(super.$linkableKeys(), 'schema'); + } +} diff --git a/frontend/module/main.ts b/frontend/module/main.ts new file mode 100644 index 000000000000..b2a116a9e8f8 --- /dev/null +++ b/frontend/module/main.ts @@ -0,0 +1,82 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { Injector, NgModule } from '@angular/core'; +import { OpenprojectCommonModule } from 'core-app/modules/common/openproject-common.module'; + +import { GitlabTabComponent } from './gitlab-tab/gitlab-tab.component'; +import { TabHeaderComponent } from './tab-header/tab-header.component'; +import { TabMrsComponent } from './tab-mrs/tab-mrs.component'; +import { GitActionsMenuDirective } from './git-actions-menu/git-actions-menu.directive'; +import { GitActionsMenuComponent } from './git-actions-menu/git-actions-menu.component'; +import { WorkPackagesGitlabMrsService } from './tab-mrs/wp-gitlab-mrs.service'; +import { MergeRequestComponent } from './merge-request/merge-request.component'; +import { WorkPackageTabsService } from "core-components/wp-tabs/services/wp-tabs/wp-tabs.service"; +import { OpenprojectTabsModule } from "core-app/modules/common/tabs/openproject-tabs.module"; + +export function initializeGitlabIntegrationPlugin(injector:Injector) { + const wpTabService = injector.get(WorkPackageTabsService); + wpTabService.register({ + component: GitlabTabComponent, + name: I18n.t('js.gitlab_integration.work_packages.tab_name'), + id: 'gitlab', + displayable: (workPackage) => !!workPackage.gitlab, + }); +} + + +@NgModule({ + imports: [ + OpenprojectCommonModule, + OpenprojectTabsModule, + ], + providers: [ + WorkPackagesGitlabMrsService, + ], + declarations: [ + GitlabTabComponent, + TabHeaderComponent, + TabMrsComponent, + GitActionsMenuDirective, + GitActionsMenuComponent, + MergeRequestComponent, + ], + exports: [ + GitlabTabComponent, + TabHeaderComponent, + TabMrsComponent, + GitActionsMenuDirective, + GitActionsMenuComponent, + ], +}) +export class PluginModule { + constructor(injector:Injector) { + initializeGitlabIntegrationPlugin(injector); + } +} diff --git a/frontend/module/merge-request/merge-request.component.html b/frontend/module/merge-request/merge-request.component.html new file mode 100644 index 000000000000..94a4d9ad7693 --- /dev/null +++ b/frontend/module/merge-request/merge-request.component.html @@ -0,0 +1,69 @@ + + +
+ +
+ {{ text.label_created_by }} + MR author avatar + + . + + + + {{ text.label_last_updated_on }} + + . +
+ + + + {{state}} + + +
+ + + {{label.title}} + + +
+ + diff --git a/frontend/module/merge-request/merge-request.component.sass b/frontend/module/merge-request/merge-request.component.sass new file mode 100644 index 000000000000..f29d25befc9f --- /dev/null +++ b/frontend/module/merge-request/merge-request.component.sass @@ -0,0 +1,123 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +@import "helpers" + +:host, +.op-merge-request + display: grid + grid-template-columns: auto 1fr auto auto + grid-template-areas: "link link link link" "title title title state" "info info info info" "pipeline-label pipeline-label pipeline-label pipeline-label" "pipeline pipeline pipeline pipeline" + margin-bottom: 11px + padding-bottom: 11px + border-bottom: 1px solid #dddddd + + &:last-child + border-bottom: none + + &--title + @include text-shortener + font-weight: bold + grid-area: title + // have the same line height as the status "button" next to it + line-height: 32px + margin-right: 20px + + &--avatar + grid-area: info + + &--info + display: block + grid-area: info + margin-bottom: 3px + font-size: 0.9rem + grid-area: info + color: var(--gray-dark) + // The mini avatar is much higher than the text line. Compensate it. + margin-top: -4px + + &--labels + display: block + grid-area: info + margin-bottom: 3px + font-size: 0.9rem + grid-area: info + color: var(--gray-dark) + margin-top: 20px + + &--label + display: inline-block + padding: 6px 9px + border-radius: 16px + border: 1px solid #fff + color: #fff + font-size: 0.8rem + text-transform: capitalize + + &--date + grid-area: info + + &--link + grid-area: link + margin-top: 6px + font-size: 0.9rem + + &--state + grid-area: state + display: inline-block + padding: 7px 11px + border-radius: 5px + border: 1px solid #fff + color: #fff + font-size: 0.9rem + font-weight: 700 + text-transform: capitalize + + &_draft + background-color: #6a737d + + &_ready + background-color: #108548 + + &_merged + background-color: #1f75cb + + &_closed + background-color: #dd2b0e + + &--pipeline-label + grid-area: pipeline-label + margin-top: 12px + font-size: 0.9rem + font-weight: bold + + &--pipeline + margin-top: 12px + margin-left: 0 + grid-area: pipeline diff --git a/frontend/module/merge-request/merge-request.component.ts b/frontend/module/merge-request/merge-request.component.ts new file mode 100644 index 000000000000..beb7c0b7a987 --- /dev/null +++ b/frontend/module/merge-request/merge-request.component.ts @@ -0,0 +1,117 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { Component, Input } from '@angular/core'; +import {PathHelperService} from 'core-app/modules/common/path-helper/path-helper.service'; +import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; +import { GitlabPipelineResource } from '../hal/resources/gitlab-pipelines-resource'; +import { IGitlabMergeRequestResource } from "../../../../../../../../modules/gitlab_integration/frontend/module/typings"; + +@Component({ + selector: 'gitlab-merge-request', + templateUrl: './merge-request.component.html', + styleUrls: [ + './merge-request.component.sass', + './mr-pipeline.component.sass', + ], + host: { class: 'op-merge-request' } +}) + +export class MergeRequestComponent { + @Input() public mergeRequest:IGitlabMergeRequestResource; + + public text = { + label_created_by: this.I18n.t('js.label_created_by'), + label_last_updated_on: this.I18n.t('js.label_last_updated_on'), + label_details: this.I18n.t('js.label_details'), + label_pipelines: this.I18n.t('js.gitlab_integration.gitlab_pipelines'), + }; + + constructor(readonly PathHelper:PathHelperService, + readonly I18n:I18nService) { + } + + get state() { + + if (this.mergeRequest.state === 'opened') { + return (this.mergeRequest.draft ? 'draft' : 'ready'); + } else { + return(this.mergeRequest.merged ? 'merged' : 'closed'); + } + } + + public pipelineStateText(pipeline:GitlabPipelineResource) { + /* Github apps can *optionally* add an output object (and a title) which is the most relevant information to display. + If that is not present, we can display the conclusion (which is present only on finished runs). + If that is not present, we can always fall back to the status. */ + return(pipeline.status); + } + + public pipelineState(pipeline:GitlabPipelineResource) { + return(pipeline.status); + } + + public pipelineStateIcon(pipeline:GitlabPipelineResource) { + switch (this.pipelineState(pipeline)) { + case 'success': { + return 'checkmark' + } + case 'queued': { + return 'getting-started' + } + case 'in_progress': { + return 'loading1' + } + case 'failure': { + return 'cancel' + } + case 'timed_out': { + return 'reminder' + } + case 'action_required': { + return 'warning' + } + case 'stale': { + return 'not-supported' + } + case 'skipped': { + return 'redo' + } + case 'neutral': { + return 'minus1' + } + case 'cancelled': { + return 'minus1' + } + default: { + return 'not-supported' + } + } + } +} diff --git a/frontend/module/merge-request/mr-pipeline.component.sass b/frontend/module/merge-request/mr-pipeline.component.sass new file mode 100644 index 000000000000..a346c9a5bdfc --- /dev/null +++ b/frontend/module/merge-request/mr-pipeline.component.sass @@ -0,0 +1,90 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ +.op-mr-pipeline + display: grid + grid-row: span 4 + grid-template-columns: 28px 33px 1fr auto + grid-template-areas: "pipeline-state-icon pipeline-avatar pipeline-info pipeline-details" + list-style-type: none + border: 1px solid #dddddd + padding: 0.3rem 1rem + background: rgba(0, 0, 0, 0.05) + font-size: 0.9rem + + &:first-child + border-top-right-radius: 5px + border-top-left-radius: 5px + + &:last-child + border-bottom-right-radius: 5px + border-bottom-left-radius: 5px + + &--avatar img + grid-area: pipeline-avatar + display: inline-block + width: 22px + height: 22px + margin-right: 5px + border-radius: var(--user-avatar-border-radius) + + &--info + grid-area: pipeline-info + + &--state + color: var(--gray-dark) + font-style: italic + margin-left: 1em + + &--state-icon + grid-area: pipeline-state-icon + + &_queued + color: cadetblue + + &_in_progress + color: orange + + &_success + color: green + + &_failure, + &_timed_out, + &_action_required, + &_stale + color: red + + &_skipped, + &_neutral, + &_cancelled + color: gray + color: gray + color: gray + + &--details + grid-area: pipeline-details diff --git a/frontend/module/tab-header/styles/tab-header.sass b/frontend/module/tab-header/styles/tab-header.sass new file mode 100644 index 000000000000..16ba6d0b05b0 --- /dev/null +++ b/frontend/module/tab-header/styles/tab-header.sass @@ -0,0 +1,47 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +.gitlab-mr-header + display: flex + flex-wrap: wrap-reverse + justify-content: flex-end + + border-bottom: 1px solid #ddd + + margin: 1.5rem 0 0.8rem 0 + + .title + flex: 1 1 auto + border-bottom: 0 + margin: 0 + padding: 0 + font-weight: bold + font-size: 1rem + line-height: 32px + text-transform: uppercase \ No newline at end of file diff --git a/frontend/module/tab-header/tab-header.component.ts b/frontend/module/tab-header/tab-header.component.ts new file mode 100644 index 000000000000..c3ac939062a8 --- /dev/null +++ b/frontend/module/tab-header/tab-header.component.ts @@ -0,0 +1,54 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import {Component, Input} from '@angular/core'; +import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; +import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; + +@Component({ + selector: 'tab-header', + templateUrl: './tab-header.template.html', + styleUrls: [ + './styles/tab-header.sass' + ] +}) +export class TabHeaderComponent { + @Input() public workPackage:WorkPackageResource; + + public text = { + title: this.I18n.t('js.gitlab_integration.tab_header.title'), + // createPrButtonLabel: this.I18n.t('js.gitlab_integration.tab_header.create_mr.label'), + // createPrButtonDescription: this.I18n.t('js.gitlab_integration.tab_header.create_mr.description'), + gitMenuLabel: this.I18n.t('js.gitlab_integration.tab_header.copy_menu.label'), + gitMenuDescription: this.I18n.t('js.gitlab_integration.tab_header.copy_menu.description'), + }; + + constructor(readonly I18n:I18nService) { + } +} diff --git a/frontend/module/tab-header/tab-header.template.html b/frontend/module/tab-header/tab-header.template.html new file mode 100644 index 000000000000..d35fa8317365 --- /dev/null +++ b/frontend/module/tab-header/tab-header.template.html @@ -0,0 +1,21 @@ +
+

+ + {{text.title}} +

+
    +
  • + +
  • +
+
diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/frontend/module/tab-mrs/tab-mrs.component.ts new file mode 100644 index 000000000000..e7325e4d221a --- /dev/null +++ b/frontend/module/tab-mrs/tab-mrs.component.ts @@ -0,0 +1,70 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import {Component, Input, OnInit} from '@angular/core'; +import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; +import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; +import { APIV3Service } from 'core-app/modules/apiv3/api-v3.service'; +import { HalResourceService } from 'core-app/modules/hal/services/hal-resource.service'; +import { CollectionResource } from 'core-app/modules/hal/resources/collection-resource'; +import { ChangeDetectorRef } from '@angular/core'; +import { IGitlabMergeRequestResource } from "../../../../../../../../modules/gitlab_integration/frontend/module/typings"; + +@Component({ + selector: 'tab-mrs', + templateUrl: './tab-mrs.template.html', + host: { class: 'op-mrs' } +}) +export class TabMrsComponent implements OnInit { + @Input() public workPackage:WorkPackageResource; + + public mergeRequests:IGitlabMergeRequestResource[] = []; + + constructor( + readonly I18n:I18nService, + readonly apiV3Service:APIV3Service, + readonly halResourceService:HalResourceService, + readonly changeDetector:ChangeDetectorRef, + ) {} + + ngOnInit(): void { + const mergeRequestsPath = this.apiV3Service.work_packages.id({id: this.workPackage.id })?.gitlab_merge_requests.path; + + this.halResourceService + .get>(mergeRequestsPath) + .subscribe((value) => { + this.mergeRequests = value.elements; + this.changeDetector.detectChanges(); + }); + } + + public getEmptyText() { + return this.I18n.t('js.gitlab_integration.tab_mrs.empty',{ wp_id: this.workPackage.id }); + } +} diff --git a/frontend/module/tab-mrs/tab-mrs.template.html b/frontend/module/tab-mrs/tab-mrs.template.html new file mode 100644 index 000000000000..43ab49c22624 --- /dev/null +++ b/frontend/module/tab-mrs/tab-mrs.template.html @@ -0,0 +1,5 @@ + +

+
+ + diff --git a/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts b/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts new file mode 100644 index 000000000000..915455e29078 --- /dev/null +++ b/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts @@ -0,0 +1,52 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { WorkPackageResource } from 'core-app/modules/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; +import { Injectable } from '@angular/core'; +import { ConfigurationService } from 'core-app/modules/common/config/configuration.service'; +import { WorkPackageLinkedResourceCache } from 'core-components/wp-single-view-tabs/wp-linked-resource-cache.service'; + +@Injectable() +export class WorkPackagesGitlabMrsService extends WorkPackageLinkedResourceCache { + + constructor(public ConfigurationService:ConfigurationService) { + super(); + } + + protected load(workPackage:WorkPackageResource):Promise { + return workPackage.gitlab_merge_requests.$update().then((data:any) => { + return this.sortList(data.elements); + }); + } + + protected sortList(mergeRequests:HalResource[], attr = 'createdAt'):HalResource[] { + return _.sortBy(_.flatten(mergeRequests), attr); + } +} diff --git a/frontend/module/typings.d.ts b/frontend/module/typings.d.ts new file mode 100644 index 000000000000..64ff69f93fe7 --- /dev/null +++ b/frontend/module/typings.d.ts @@ -0,0 +1,77 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2021 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { TabDefinition } from "core-app/modules/common/tabs/tab.interface"; +import { HalResourceClass } from "core-app/modules/hal/resources/hal-resource"; + +export interface ISnippet { + id: string; + name: string; + textToCopy: ()=>string +} + +export interface IGitlabMergeRequestResource extends HalResourceClass { + body?:{ + format?:string; + raw?:string; + html?:string; + }, + createdAt?:string; + draft?:boolean; + gitlabUpdatedAt?:string; + htmlUrl?:string; + id?:number; + labels?:string[]; + merged?:boolean; + mergedAt?:string; + mergedBy?:IGitlabUserResource; + number?:number; + repository?:string; + state?:string; + title?:string; + updatedAt?:string; + gitlabUser?:IGitlabUserResource; + pipelines?:IGitlabPipelineResource[]; +} + +export interface IGitlabUserResource { + avatarUrl:string; + email:string; + login:string; +} + +export interface IGitlabPipelineResource { + userAvatarUrl:string; + completedAt:string; + detailsUrl:string; + htmlUrl:string; + name:string; + startedAt:string; + status:string; +} \ No newline at end of file diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb new file mode 100644 index 000000000000..b643c0267317 --- /dev/null +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb @@ -0,0 +1,40 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module GitlabMergeRequests + class GitlabMergeRequestCollectionRepresenter < ::API::Decorators::Collection + self.to_eager_load = ::API::V3::GitlabMergeRequests::GitlabMergeRequestRepresenter.to_eager_load + end + end + end +end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb new file mode 100644 index 000000000000..f1cce2d2cdfe --- /dev/null +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb @@ -0,0 +1,108 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'roar/decorator' +require 'roar/json/hal' + +module API + module V3 + module GitlabMergeRequests + class GitlabMergeRequestRepresenter < ::API::Decorators::Single + include API::Caching::CachedRepresenter + include API::Decorators::DateProperty + include API::Decorators::FormattableProperty + include API::Decorators::LinkedResource + + def initialize(model, current_user:, **_opts) + # We force `embed_links` so that github_user and github_check_runs + # are embedded and we can avoid having separate endpoints. + super(model, current_user: current_user, embed_links: true) + end + + cached_representer key_parts: %i[gitlab_user merged_by] + + property :id + + property :number + + property :gitlab_html_url, as: :htmlUrl + + property :state, + render_nil: true + + property :repository, + render_nil: true + + date_time_property :gitlab_updated_at, + render_nil: true, + setter: ->(*) { nil } + + property :title, + render_nil: true + + formattable_property :body, + render_nil: true + + property :draft, + render_nil: true + + property :merged, + render_nil: true + + property :labels + + associated_resource :gitlab_user, + representer: ::API::V3::GitlabMergeRequests::GitlabUserRepresenter, + link_title_attribute: :gitlab_name + + associated_resource :merged_by, + representer: ::API::V3::GitlabMergeRequests::GitlabUserRepresenter, + v3_path: :gitlab_user, + link_title_attribute: :gitlab_name + + # TODO: pending until get the list of statuses... + associated_resources :latest_pipelines, + as: :pipelines, + representer: ::API::V3::GitlabMergeRequests::GitlabPipelineRepresenter, + v3_path: :gitlab_pipeline, + link_title_attribute: :name + + date_time_property :created_at + + date_time_property :updated_at + + def _type + 'GitlabMergeRequest' + end + + self.to_eager_load = %i[gitlab_user merged_by] + end + end + end +end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb new file mode 100644 index 000000000000..28c3920c258f --- /dev/null +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb @@ -0,0 +1,53 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module GitlabMergeRequests + class GitlabMergeRequestsByWorkPackageAPI < ::API::OpenProjectAPI + after_validation do + authorize(:show_gitlab_content, context: @work_package.project) + @gitlab_merge_requests = @work_package.gitlab_merge_requests + end + + resources :gitlab_merge_requests do + get do + path = api_v3_paths.gitlab_merge_requests_by_work_package(@work_package.id) + GitlabMergeRequestCollectionRepresenter.new(@gitlab_merge_requests, + @gitlab_merge_requests.count, + self_link: path, + current_user: current_user) + end + end + end + end + end +end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb new file mode 100644 index 000000000000..c1ec18dd818d --- /dev/null +++ b/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb @@ -0,0 +1,59 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'roar/decorator' +require 'roar/json/hal' + +module API + module V3 + module GitlabMergeRequests + class GitlabPipelineRepresenter < ::API::Decorators::Single + include API::Caching::CachedRepresenter + include API::Decorators::DateProperty + + self_link id_attribute: :id, + title_getter: ->(*) { nil } + + property :gitlab_html_url, as: :htmlUrl + property :gitlab_user_avatar_url, as: :userAvatarUrl + property :name + property :status + property :details_url + property :ci_details + + date_time_property :started_at + date_time_property :completed_at + + def _type + 'GitlabPipeline' + end + end + end + end +end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb new file mode 100644 index 000000000000..914fce5146d2 --- /dev/null +++ b/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb @@ -0,0 +1,52 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'roar/decorator' +require 'roar/json/hal' + +module API + module V3 + module GitlabMergeRequests + class GitlabUserRepresenter < ::API::Decorators::Single + include API::Caching::CachedRepresenter + + self_link id_attribute: :id, + title_getter: ->(*) { nil } + + property :gitlab_name, as: :login + property :gitlab_email, as: :email + property :gitlab_avatar_url, as: :avatarUrl + + def _type + 'GitlabUser' + end + end + end + end +end diff --git a/lib/open_project/gitlab_integration.rb b/lib/open_project/gitlab_integration.rb index b06edeea833c..c524473ec832 100644 --- a/lib/open_project/gitlab_integration.rb +++ b/lib/open_project/gitlab_integration.rb @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 8bf65c1ca944..a3c16c6f699e 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -27,8 +28,11 @@ #++ require 'open_project/plugins' + +require_relative './patches/api/work_package_representer' require_relative './notification_handlers' require_relative './hook_handler' +require_relative './services' module OpenProject::GitlabIntegration class Engine < ::Rails::Engine @@ -37,7 +41,7 @@ class Engine < ::Rails::Engine include OpenProject::Plugins::ActsAsOpEngine register 'openproject-gitlab_integration', - :author_url => 'https://github.com/btey/openproject-gitlab-integration', + :author_url => 'https://github.com/btey/openproject', bundled: true @@ -56,6 +60,40 @@ class Engine < ::Rails::Engine &NotificationHandlers.method(:issue_hook)) ::OpenProject::Notifications.subscribe('gitlab.push_hook', &NotificationHandlers.method(:push_hook)) + ::OpenProject::Notifications.subscribe('gitlab.pipeline_hook', + &NotificationHandlers.method(:pipeline_hook)) + end + + initializer 'gitlab.permissions' do + OpenProject::AccessControl.map do |ac_map| + ac_map.project_module(:gitlab, dependencies: :work_package_tracking) do |pm_map| + pm_map.permission(:show_gitlab_content, {}, {}) + end + end + end + + extend_api_response(:v3, :work_packages, :work_package, + &::OpenProject::GitlabIntegration::Patches::API::WorkPackageRepresenter.extension) + + add_api_path :gitlab_merge_requests_by_work_package do |id| + "#{work_package(id)}/gitlab_merge_requests" + end + + add_api_path :gitlab_user do |id| + "gitlab_users/#{id}" + end + + add_api_path :gitlab_pipeline do |id| + "gitlab_pipeline/#{id}" + end + + add_api_endpoint 'API::V3::WorkPackages::WorkPackagesAPI', :id do + mount ::API::V3::GitlabMergeRequests::GitlabMergeRequestsByWorkPackageAPI + end + + config.to_prepare do + # Register the cron job to clean up old gitlab merge requests + ::Cron::CronJob.register! ::Cron::ClearOldMergeRequestsJob end end diff --git a/lib/open_project/gitlab_integration/hook_handler.rb b/lib/open_project/gitlab_integration/hook_handler.rb index 2b330ec9f8ac..fc163e17ea0f 100644 --- a/lib/open_project/gitlab_integration/hook_handler.rb +++ b/lib/open_project/gitlab_integration/hook_handler.rb @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -29,7 +30,13 @@ module OpenProject::GitlabIntegration class HookHandler # List of the gitlab events we can handle. - KNOWN_EVENTS = %w[push_hook issue_hook note_hook merge_request_hook].freeze + KNOWN_EVENTS = %w[ + push_hook + issue_hook + note_hook + merge_request_hook + pipeline_hook + ].freeze # A gitlab webhook happened. # We need to check validity of the data and send a Notification @@ -50,13 +57,9 @@ def process(hook, request, params, user) .merge('open_project_user_id' => user.id, 'gitlab_event' => event_type) - OpenProject::Notifications.send(event_name(event_type), payload) + OpenProject::Notifications.send("gitlab.#{event_type}", payload) return 200 end - - private def event_name(gitlab_event_name) - "gitlab.#{gitlab_event_name}" - end end end diff --git a/lib/open_project/gitlab_integration/notification_handler/helper.rb b/lib/open_project/gitlab_integration/notification_handler/helper.rb new file mode 100644 index 000000000000..dec15c25dad3 --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/helper.rb @@ -0,0 +1,151 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + module Helper + + ## + # Parses the given source string and returns a list of work_package ids + # which it finds and should be considered public or private. + # WorkPackages are identified by their URL. + # Params: + # source: string + # Returns: + # Array + def extract_work_package_ids(text, kind = "") + # matches the following things (given that `Setting.host_name` equals 'www.openproject.org') + # - http://www.openproject.org/wp/1234 + # - https://www.openproject.org/wp/1234 + # - http://www.openproject.org/work_packages/1234 + # - https://www.openproject.org/subdirectory/work_packages/1234 + # Or with the following prefix: OP# PP# + # e.g.,: This is a reference to OP#1234 + # For private comments you can use the prefix: PP# + host_name = Regexp.escape(Setting.host_name) + if kind == 'private' + wp_regex = /PP#(\d+)/ + elsif kind != 'note' + wp_regex = /OP#(\d+)|PP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ + else + wp_regex = /OP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ + end + String(text) + .scan(wp_regex) + .map { |first, second| (first || second).to_i } + .select(&:positive?) + .uniq + end + + ## + # Given a list of work package ids this methods returns all work packages that match those ids + # and are visible by the given user. + # Params: + # - Array: An list of WorkPackage ids + # - User: The user who may (or may not) see those WorkPackages + # Returns: + # - Array + def find_visible_work_packages(ids, user) + WorkPackage + .includes(:project) + .where(id: ids) + .select { |wp| user.allowed_to?(:add_work_package_notes, wp.project) } + end + + # Returns a list of `WorkPackage`s that were referenced in the `text` and are visible to the given `user`. + def find_mentioned_work_packages(text, user, kind = "") + find_visible_work_packages(extract_work_package_ids(text, kind), user) + end + + # Returns a list of `WorkPackage`s that were excluded in the `text`. + def find_excluded_work_packages(text, user) + find_visible_work_packages(extract_work_package_ids(text, 'private'), user) + end + + ## + # Adds comments to the given WorkPackages. + def comment_on_referenced_work_packages(work_packages, user, notes) + return if notes.nil? + work_packages.each do |work_package| + ::WorkPackages::UpdateService + .new(user: user, model: work_package) + .call(journal_notes: notes, send_notifications: false) + end + end + + ## + # Adds comments to the given WorkPackages. + def status_on_referenced_work_packages(work_packages, user, status) + work_packages.each do |work_package| + ::WorkPackages::UpdateService + .new(user: user, model: work_package) + .call(status_id: status) + end + end + + ## + # A wapper around a ruby Hash to access webhook payloads. + # All methods called on it are converted to `.fetch` hash-access, raising an error if the string-key does not exist. + # If the method ends with a question mark, e.g. "comment?" not error is raised if the key does not exist. + # If the fetched value is again a hash, the value is wrapped into a new payload object. + class Payload + def initialize(payload) + @payload = payload + end + + def to_h + @payload.dup + end + + def method_missing(name, *args, &block) + super unless args.empty? && block.nil? + + value = if name.end_with?('?') + @payload.fetch(name.to_s[..-2], nil) + else + @payload.fetch(name.to_s) + end + + return Payload.new(value) if value.is_a?(Hash) + + value + end + + def respond_to_missing?(_method_name, _include_private = false) + true + end + end + + def wrap_payload(payload) + Payload.new(payload) + end + + end + end +end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb b/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb new file mode 100644 index 000000000000..8688db056002 --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb @@ -0,0 +1,71 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + ## + # Handles Gitlab issue notifications. + class IssueHook + include OpenProject::GitlabIntegration::NotificationHandler::Helper + + def process(payload_params) + @payload = wrap_payload(payload_params) + user = User.find_by_id(payload.open_project_user_id) + text = payload.object_attributes.title + ' - ' + payload.object_attributes.description + work_packages = find_mentioned_work_packages(text, user) + notes = generate_notes(payload) + comment_on_referenced_work_packages(work_packages, user, notes) + end + + private + + attr_reader :payload + + def generate_notes(payload) + accepted_actions = %w[open reopen close] + + key_action = { + 'open' => 'opened', + 'reopen' => 'reopened', + 'close' => 'closed' + }[payload.object_attributes.action] + + return nil unless accepted_actions.include? payload.object_attributes.action + I18n.t("gitlab_integration.issue_#{key_action}_referenced_comment", + :issue_number => payload.object_attributes.iid, + :issue_title => payload.object_attributes.title, + :issue_url => payload.object_attributes.url, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + end + end + end +end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb new file mode 100644 index 000000000000..3ac435a3eb44 --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb @@ -0,0 +1,109 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + ## + # Handles Gitlab merge request notifications. + class MergeRequestHook + include OpenProject::GitlabIntegration::NotificationHandler::Helper + + def process(payload_params) + update_status_on_new_mr = false # true if you only reference one merge by work_package, else false. + update_status_on_merged = false # true if you only reference one merge by work_package, else false. + + accepted_actions = %w[open update reopen] + accepted_actions_for_comments = %w[open reopen] + accepted_states = %w[closed merged] + + @payload = wrap_payload(payload_params) + return unless (accepted_actions.include? payload.object_attributes.action) || (accepted_states.include? payload.object_attributes.state) + + user = User.find_by_id(payload.open_project_user_id) + text = payload.object_attributes.title + work_packages = find_mentioned_work_packages(text, user) + notes = generate_notes(payload) + + if (accepted_actions_for_comments.include? payload.object_attributes.action) || (accepted_states.include? payload.object_attributes.state) + comment_on_referenced_work_packages(work_packages, user, notes) + if payload.object_attributes.state == 'opened' && update_status_on_new_mr + status_on_referenced_work_packages(work_packages, user, 7) + elsif payload.object_attributes.state == 'merged' && update_status_on_merged + status_on_referenced_work_packages(work_packages, user, 8) + end + end + upsert_merge_request(work_packages) + end + + private + + attr_reader :payload + + def generate_notes(payload) + key = { + 'opened' => 'opened', + 'reopened' => 'reopened', + 'closed' => 'closed', + 'merged' => 'merged', + 'edited' => 'referenced', + 'referenced' => 'referenced' + }[payload.object_attributes.state] + + key_action = { + 'reopen' => 'reopened' + }[payload.object_attributes.action] + + return nil unless key + + I18n.t("gitlab_integration.merge_request_#{key_action ? key_action : key}_comment", + :mr_number => payload.object_attributes.iid, + :mr_title => payload.object_attributes.title, + :mr_url => payload.object_attributes.url, + :repository => payload.repository.name, + :repository_url => payload.repository.url, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + end + + def merge_request + @merge_request ||= GitlabMergeRequest + .where(gitlab_id: payload.object_attributes.iid) + .or(GitlabMergeRequest.where(gitlab_html_url: payload.object_attributes.url)) + .take + end + + def upsert_merge_request(work_packages) + return if work_packages.empty? && merge_request.nil? + OpenProject::GitlabIntegration::Services::UpsertMergeRequest.new.call(payload, + work_packages: work_packages) + end + + end + end +end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb new file mode 100644 index 000000000000..049998d40ad5 --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb @@ -0,0 +1,146 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + ## + # Handles Gitlab comment notifications. + class NoteHook + include OpenProject::GitlabIntegration::NotificationHandler::Helper + + # TODO: this can be more refactored and simplified... + def process(payload_params) + @payload = wrap_payload(payload_params) + user = User.find_by_id(payload.open_project_user_id) + text = payload.object_attributes.note + work_packages = find_mentioned_work_packages(text, user, payload.object_kind) + if work_packages.empty? && payload.object_attributes.noteable_type == 'Issue' + text = payload.issue.title + ' - ' + payload.object_attributes.note + work_packages = find_mentioned_work_packages(text, user, payload.object_kind) + work_packages_excluded = find_excluded_work_packages(text, user) + work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? + return if work_packages.empty? + notes = generate_notes(payload, 'comment') + elsif work_packages.empty? && payload.object_attributes.noteable_type == 'Snippet' + text = payload.snippet.title + ' - ' + payload.object_attributes.note + work_packages = find_mentioned_work_packages(text, user, payload.object_kind) + work_packages_excluded = find_excluded_work_packages(text, user) + work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? + return if work_packages.empty? + notes = generate_notes(payload, 'reference') + elsif work_packages.empty? && payload.object_attributes.noteable_type == 'MergeRequest' + text = payload.merge_request.title + ' - ' + payload.object_attributes.note + work_packages = find_mentioned_work_packages(text, user, payload.object_kind) + work_packages_excluded = find_excluded_work_packages(text, user) + work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? + return if work_packages.empty? + notes = generate_notes(payload, 'comment') + else + notes = generate_notes(payload, 'reference') + end + comment_on_referenced_work_packages(work_packages, user, notes) + end + + private + + attr_reader :payload + + # TODO: add key list to simplify the code... + def generate_notes(payload, note_type) + if payload.object_attributes.noteable_type == 'Commit' + commit_id = payload.commit.id + I18n.t("gitlab_integration.note_commit_referenced_comment", + :commit_id => commit_id[0, 8], + :commit_url => payload.object_attributes.url, + :commit_note => payload.object_attributes.note, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + elsif payload.object_attributes.noteable_type == 'MergeRequest' + if note_type == 'comment' + I18n.t("gitlab_integration.note_mr_commented_comment", + :mr_number => payload.merge_request.iid, + :mr_title => payload.merge_request.title, + :mr_url => payload.object_attributes.url, + :mr_note => payload.object_attributes.note, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + elsif note_type == 'reference' + I18n.t("gitlab_integration.note_mr_referenced_comment", + :mr_number => payload.merge_request.iid, + :mr_title => payload.merge_request.title, + :mr_url => payload.object_attributes.url, + :mr_note => payload.object_attributes.note, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + end + elsif payload.object_attributes.noteable_type == 'Issue' + if note_type == 'comment' + I18n.t("gitlab_integration.note_issue_commented_comment", + :issue_number => payload.issue.iid, + :issue_title => payload.issue.title, + :issue_url => payload.object_attributes.url, + :issue_note => payload.object_attributes.note, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + elsif note_type == 'reference' + I18n.t("gitlab_integration.note_issue_referenced_comment", + :issue_number => payload.issue.iid, + :issue_title => payload.issue.title, + :issue_url => payload.object_attributes.url, + :issue_note => payload.object_attributes.note, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + end + elsif payload.object_attributes.noteable_type == 'Snippet' + I18n.t("gitlab_integration.note_snippet_referenced_comment", + :snippet_number => payload.snippet.id, + :snippet_title => payload.snippet.title, + :snippet_url => payload.object_attributes.url, + :snippet_note => payload.object_attributes.note, + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user.name, + :gitlab_user_url => payload.user.avatar_url) + else + return nil + end + end + end + end +end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb new file mode 100644 index 000000000000..85cdec1ebfaa --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb @@ -0,0 +1,65 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + ## + # Handles Gitlab pipeline notifications. + class PipelineHook + include OpenProject::GitlabIntegration::NotificationHandler::Helper + + def process(payload_params) + @payload = wrap_payload(payload_params) + + return unless associated_with_mr? + merge_request = find_merge_request + return unless merge_request + + # disabled until gitlab issue resolution + # OpenProject::GitlabIntegration::Services::UpsertPipeline.new.call( + # payload, + # merge_request: merge_request + # ) + end + + private + + attr_reader :payload + + def associated_with_mr? + payload.merge_request.iid?.present? + end + + def find_merge_request + gitlab_id = payload.merge_request.iid + GitlabMergeRequest.find_by(gitlab_id: gitlab_id) + end + end + end +end diff --git a/lib/open_project/gitlab_integration/notification_handler/push_hook.rb b/lib/open_project/gitlab_integration/notification_handler/push_hook.rb new file mode 100644 index 000000000000..83f9af261136 --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/push_hook.rb @@ -0,0 +1,67 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + ## + # Handles Gitlab commit notifications. + class PushHook + include OpenProject::GitlabIntegration::NotificationHandler::Helper + + def process(payload_params) + @payload = wrap_payload(payload_params) + return nil unless payload.object_kind == 'push' + payload.commits.each do |commit| + user = User.find_by_id(payload.open_project_user_id) + text = commit['title'] + " - " + commit['message'] + work_packages = find_mentioned_work_packages(text, user) + notes = generate_notes(commit, payload) + comment_on_referenced_work_packages(work_packages, user, notes) + end + end + + private + + attr_reader :payload + + def generate_notes(commit, payload) + commit_id = commit['id'] + I18n.t("gitlab_integration.push_single_commit_comment", + :commit_number => commit_id[0, 8], + :commit_note => commit['message'], + :commit_url => commit['url'], + :commit_timestamp => commit['timestamp'], + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user_name, + :gitlab_user_url => payload.user_avatar) + end + end + end +end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/lib/open_project/gitlab_integration/notification_handlers.rb index 23f0ff424e71..9049ca6deb0a 100644 --- a/lib/open_project/gitlab_integration/notification_handlers.rb +++ b/lib/open_project/gitlab_integration/notification_handlers.rb @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,258 +27,55 @@ # See docs/COPYRIGHT.rdoc for more details. #++ +require_relative './notification_handler/helper' +require_relative './notification_handler/issue_hook' +require_relative './notification_handler/merge_request_hook' +require_relative './notification_handler/note_hook' +require_relative './notification_handler/push_hook' + module OpenProject::GitlabIntegration ## # Handles gitlab-related notifications. module NotificationHandlers - - def self.merge_request_hook(payload) - accepted_actions = %w[open] - accepted_states = %w[closed merged] - return unless (accepted_actions.include? payload['object_attributes']['action']) || (accepted_states.include? payload['object_attributes']['state']) - comment_on_referenced_work_packages payload['object_attributes']['title'], payload - - rescue => e - Rails.logger.error "Failed to handle merge_request_hook event: #{e} #{e.message}" - raise e - end - - def self.note_hook(payload) - comment_on_referenced_work_packages payload['object_attributes']['note'], payload - - rescue => e - Rails.logger.error "Failed to handle note_hook event: #{e} #{e.message}" - raise e - end - - def self.push_hook(payload) - push_hook_split_commits payload - - rescue => e - Rails.logger.error "Failed to handle push_hook event: #{e} #{e.message}" - raise e - end - - def self.issue_hook(payload) - comment_on_referenced_work_packages payload['object_attributes']['title'] + ' - ' + payload['object_attributes']['description'], payload - - rescue => e - Rails.logger.error "Failed to handle issue_hook event: #{e} #{e.message}" - raise e - end - - def self.push_hook_split_commits(payload) - return nil unless payload['object_kind'] == 'push' - payload[:commits].each do |commit| - # concatenated to check title and description in one step - text = commit['title'] + " - " + commit['message'] - comment_on_referenced_work_packages text, payload, commit + class << self + def merge_request_hook(payload) + with_logging('merge_request_hook') do + OpenProject::GitlabIntegration::NotificationHandler::MergeRequestHook.new.process(payload) + end end - end - def self.comment_on_referenced_work_packages(text, payload, commit = nil) - user = User.find_by_id(payload['open_project_user_id']) - wp_ids = extract_work_package_ids(text) - wps = find_visible_work_packages(wp_ids, user) - if payload['object_kind'] == 'push' - notes = notes_for_push_payload(commit, payload) - elsif payload['object_kind'] == 'issue' - notes = notes_for_issue_payload(payload) - elsif payload['object_kind'] == 'merge_request' - notes = notes_for_merge_request_payload(payload) - elsif payload['object_kind'] == 'note' - if wps.empty? && payload['object_attributes']['noteable_type'] == 'Issue' - wp_ids = extract_work_package_ids(payload['issue']['title'] + ' - ' + payload['object_attributes']['note']) - wps = find_visible_work_packages(wp_ids, user) - notes = notes_for_note_payload(payload, 'comment') - elsif wps.empty? && payload['object_attributes']['noteable_type'] == 'MergeRequest' - wp_ids = extract_work_package_ids(payload['merge_request']['title'] + ' - ' + payload['object_attributes']['note']) - wps = find_visible_work_packages(wp_ids, user) - notes = notes_for_note_payload(payload, 'comment') - else - notes = notes_for_note_payload(payload, 'reference') + def note_hook(payload) + with_logging('note_hook') do + OpenProject::GitlabIntegration::NotificationHandler::NoteHook.new.process(payload) end - else - return end - return if notes.nil? - - if payload['object_kind'] == 'merge_request' - if payload['object_attributes']['state'] == 'opened' - attributes = { journal_notes: notes, status_id: 7 } - elsif payload['object_attributes']['state'] == 'merged' - attributes = { journal_notes: notes, status_id: 8 } - else - attributes = { journal_notes: notes } + def push_hook(payload) + with_logging('push_hook') do + OpenProject::GitlabIntegration::NotificationHandler::PushHook.new.process(payload) end - else - attributes = { journal_notes: notes } - end - - wps.each do |wp| - ::WorkPackages::UpdateService - .new(user: user, model: wp) - .call(attributes.merge(send_notifications: false).symbolize_keys) end - end - - ## - # Parses the given source string and returns a list of work_package ids - # which it finds. - # WorkPackages are identified by their URL. - # Params: - # source: string - # Returns: - # Array - def self.extract_work_package_ids(source) - # matches the following things (given that `Setting.host_name` equals 'www.openproject.org') - # - http://www.openproject.org/wp/1234 - # - https://www.openproject.org/wp/1234 - # - http://www.openproject.org/work_packages/1234 - # - https://www.openproject.org/subdirectory/work_packages/1234 - # Or with the following prefix: OP# - # e.g.,: This is a reference to OP#1234 - host_name = Regexp.escape(Setting.host_name) - wp_regex = /OP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ - - source.scan(wp_regex) - .map {|first, second| (first || second).to_i } - .select { |el| el > 0 } - .uniq - end - ## - # Given a list of work package ids this methods returns all work packages that match those ids - # and are visible by the given user. - # Params: - # - Array: An list of WorkPackage ids - # - User: The user who may (or may not) see those WorkPackages - # Returns: - # - Array - def self.find_visible_work_packages(ids, user) - ids.collect do |id| - WorkPackage.includes(:project).find_by_id(id) - end.select do |wp| - wp.present? && user.allowed_to?(:add_work_package_notes, wp.project) + def issue_hook(payload) + with_logging('issue_hook') do + OpenProject::GitlabIntegration::NotificationHandler::IssueHook.new.process(payload) + end end - end - - def self.notes_for_issue_payload(payload) - return nil unless payload['object_attributes']['action'] == 'open' || payload['object_attributes']['state'] == 'closed' - I18n.t("gitlab_integration.issue_#{payload['object_attributes']['state']}_referenced_comment", - :issue_number => payload['object_attributes']['iid'], - :issue_title => payload['object_attributes']['title'], - :issue_url => payload['object_attributes']['url'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - end - def self.notes_for_push_payload(commit, payload) - commit_id = commit['id'] - I18n.t("gitlab_integration.push_single_commit_comment", - :commit_number => commit_id[0, 8], - :commit_note => commit['message'], - :commit_url => commit['url'], - :commit_timestamp => commit['timestamp'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user_name'], - :gitlab_user_url => payload['user_avatar']) - end - - def self.notes_for_merge_request_payload(payload) - key = { - 'opened' => 'opened', - 'reopened' => 'opened', - 'closed' => 'closed', - 'merged' => 'merged', - 'edited' => 'referenced', - 'referenced' => 'referenced' - }[payload['object_attributes']['state']] - - return nil unless key + def pipeline_hook(payload) + with_logging('pipeline_hook') do + OpenProject::GitlabIntegration::NotificationHandler::PipelineHook.new.process(payload) + end + end - I18n.t("gitlab_integration.merge_request_#{key}_comment", - :mr_number => payload['object_attributes']['id'], - :mr_title => payload['object_attributes']['title'], - :mr_url => payload['object_attributes']['url'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['url'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - end + private - def self.notes_for_note_payload(payload, note_type) - if payload['object_attributes']['noteable_type'] == 'Commit' - commit_id = payload['commit']['id'] - I18n.t("gitlab_integration.note_commit_referenced_comment", - :commit_id => commit_id[0, 8], - :commit_url => payload['object_attributes']['url'], - :commit_note => payload['object_attributes']['note'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - elsif payload['object_attributes']['noteable_type'] == 'MergeRequest' - if note_type == 'comment' - I18n.t("gitlab_integration.note_mr_commented_comment", - :mr_number => payload['merge_request']['id'], - :mr_title => payload['merge_request']['title'], - :mr_url => payload['object_attributes']['url'], - :mr_note => payload['object_attributes']['note'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - elsif note_type == 'reference' - I18n.t("gitlab_integration.note_mr_referenced_comment", - :mr_number => payload['merge_request']['id'], - :mr_title => payload['merge_request']['title'], - :mr_url => payload['object_attributes']['url'], - :mr_note => payload['object_attributes']['note'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - end - elsif payload['object_attributes']['noteable_type'] == 'Issue' - if note_type == 'comment' - I18n.t("gitlab_integration.note_issue_commented_comment", - :issue_number => payload['issue']['iid'], - :issue_title => payload['issue']['title'], - :issue_url => payload['object_attributes']['url'], - :issue_note => payload['object_attributes']['note'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - elsif note_type == 'reference' - I18n.t("gitlab_integration.note_issue_referenced_comment", - :issue_number => payload['issue']['iid'], - :issue_title => payload['issue']['title'], - :issue_url => payload['object_attributes']['url'], - :issue_note => payload['object_attributes']['note'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - end - elsif payload['object_attributes']['noteable_type'] == 'Snippet' - I18n.t("gitlab_integration.note_snippet_referenced_comment", - :snippet_number => payload['snippet']['id'], - :snippet_title => payload['snippet']['title'], - :snippet_url => payload['object_attributes']['url'], - :snippet_note => payload['object_attributes']['note'], - :repository => payload['repository']['name'], - :repository_url => payload['repository']['homepage'], - :gitlab_user => payload['user']['name'], - :gitlab_user_url => payload['user']['avatar_url']) - else - return nil + def with_logging(event_hook) + yield if block_given? + rescue StandardError => e + Rails.logger.error "Failed to handle #{event_hook} from Gitlab: #{e} #{e.message}" + raise e end end end diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb new file mode 100644 index 000000000000..de310797b95e --- /dev/null +++ b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb @@ -0,0 +1,56 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject::GitlabIntegration + module Patches + module API + module WorkPackageRepresenter + module_function + + def extension + ->(*) do + link :gitlab, + cache_if: -> { current_user.allowed_to?(:show_gitlab_content, represented.project) } do + { + href: "#{work_package_path(id: represented.id)}/tabs/gitlab", + title: "gitlab" + } + end + + link :gitlab_merge_requests do + { + href: api_v3_paths.gitlab_merge_requests_by_work_package(represented.id), + title: "Gitlab merge requests" + } + end + end + end + end + end + end +end diff --git a/lib/open_project/gitlab_integration/services.rb b/lib/open_project/gitlab_integration/services.rb new file mode 100644 index 000000000000..25f735aabe71 --- /dev/null +++ b/lib/open_project/gitlab_integration/services.rb @@ -0,0 +1,32 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2021 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require_relative './services/upsert_pipeline' +require_relative './services/upsert_gitlab_user' +require_relative './services/upsert_merge_request' diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb new file mode 100644 index 000000000000..42845f14e16d --- /dev/null +++ b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb @@ -0,0 +1,60 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject::GitlabIntegration::Services + ## + # Takes user data coming from Gitlab webhook data and stores + # them as a `GitlabUser`. + # If the `GitlabUser` already exists, it is updated. + # + # Returns the upserted `GitlabUser`. + class UpsertGitlabUser + def call(payload) + GitlabUser.find_or_initialize_by(gitlab_id: payload.id) + .tap do |gitlab_user| + gitlab_user.update!(extract_params(payload)) + end + end + + private + + ## + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + def extract_params(payload) + { + gitlab_id: payload.id, + gitlab_name: payload.name, + gitlab_username: payload.username, + gitlab_email: payload.email, + gitlab_avatar_url: payload.avatar_url + } + end + end +end diff --git a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb new file mode 100644 index 000000000000..d67a17e27012 --- /dev/null +++ b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb @@ -0,0 +1,88 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject::GitlabIntegration::Services + ## + # Takes merge request data coming from Gitlab webhook data and stores + # them as a `GitlabMergeRequest`. + # If the `GitlabMergeRequest` already exists, it is updated. + # + # Returns the upserted `GitlabMergeRequest`. + class UpsertMergeRequest + def call(payload, work_packages: []) + find_or_initialize(payload).tap do |mr| + mr.update!(work_packages: mr.work_packages | work_packages, **extract_params(payload)) + end + end + + private + + def find_or_initialize(payload) + GitlabMergeRequest.find_by_gitlab_identifiers(id: payload.object_attributes.iid, + url: payload.object_attributes.url, + initialize: true) + end + + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + # rubocop:disable Metrics/AbcSize + def extract_params(payload) + { + gitlab_id: payload.object_attributes.iid, + gitlab_user: gitlab_user_id(payload.user), + number: payload.object_attributes.iid, + gitlab_html_url: payload.object_attributes.url, + gitlab_updated_at: payload.object_attributes.updated_at, + state: payload.object_attributes.state, + title: payload.object_attributes.title, + body: payload.object_attributes.description, + repository: payload.repository.name, + draft: payload.object_attributes.work_in_progress, + merged: payload.object_attributes.state == 'merged' ? true : false, + merged_by: gitlab_user_id(payload.user), + merged_at: payload.object_attributes.state == 'merged' ? payload.object_attributes.updated_at : nil, + labels: payload.labels.map { |values| extract_label_values(values) } + } + end + # rubocop:enable Metrics/AbcSize + + def extract_label_values(payload) + { + title: payload['title'], + color: payload['color'] + } + end + + def gitlab_user_id(payload) + return if payload.blank? + + ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + end + end +end diff --git a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb new file mode 100644 index 000000000000..fdca7fc2a1e0 --- /dev/null +++ b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb @@ -0,0 +1,64 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ +module OpenProject::GitlabIntegration::Services + ## + # Takes pipelines CI data coming from Gitlab webhook data and stores + # them as a `GitlabPipeline`. + # If the `GitlabPipeline` already exists, it is updated. + # + # Returns the upserted `GitlabPipeline`. + class UpsertPipeline + def call(payload, merge_request:) + GitlabPipelines.find_or_initialize_by(gitlab_id: payload.object_attributes.id) + .tap do |pipeline| + pipeline.update!(gitlab_merge_request: merge_request, **extract_params(payload)) + end + end + + private + + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + def extract_params(payload) + { + gitlab_id: payload.object_attributes.id, + gitlab_html_url: payload.project.web_url + "-/pipelines/" + payload.object_attributes.id, + project_id: payload.project.id, + gitlab_user_avatar_url: payload.user.avatar_url, + name: payload.object_attributes.status, + status: payload.object_attributes.status, + details_url: payload.project.web_url + "-/pipelines/" + payload.object_attributes.id, + # ci_details: pending until resolution of the gitlab issue, + started_at: payload.object_attributes.created_at, + completed_at: payload.object_attributes.finished_at + } + end + end +end \ No newline at end of file diff --git a/lib/openproject-gitlab_integration.rb b/lib/openproject-gitlab_integration.rb index 9e4cc60533a3..c4d802aea228 100644 --- a/lib/openproject-gitlab_integration.rb +++ b/lib/openproject-gitlab_integration.rb @@ -1,13 +1,14 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2020 the OpenProject GmbH +# Copyright (C) 2021 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 1552e73e483f..705a6daa48d3 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -1,15 +1,16 @@ # encoding: UTF-8 + Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '1.0.0' - s.authors = "Benjamin Tey" + s.version = '2.0.0' + s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" s.summary = 'OpenProject GitLab Integration' s.description = 'Integrates OpenProject and GitLab for a better workflow' s.license = 'GPLv3' - s.files = Dir["{app,config,db,doc,lib}/**/*"] + %w(README.md) + s.files = Dir["{app,config,db,frontend,lib}/**/*"] + %w(README.md) s.add_dependency "openproject-webhooks" end From 68ea542fd509ba9afdeeb6ecc17de1fe6acb3419 Mon Sep 17 00:00:00 2001 From: btey Date: Fri, 26 Nov 2021 20:49:14 +0100 Subject: [PATCH 09/69] Update README.md --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d2126a1edde..2ebe59b4074f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,16 @@ # openproject-gitlab-integration -OpenProject module for integration with Gitlab (latest release tested is 13.9.3) +## NEW VERSION 2.0.0 (pre-release) +Based on the current Github integration (OpenProject 11 & 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). + +![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) + +The events captured in the WP activity log are the same as in version 1.0, only this new version includes a UI with all linked MRs, their status, their labels and the last pipeline (pending the Gitlab issue). If you use manual installation, keep in mind that it requires precompiling the assets and updating the DB with new tables. If you want to test it, do it in a test environment, and if you want, share your results in case something needs to be corrected. + +The following readme file is pending review with updated documentation. + +## Introduction + +OpenProject module for integration with Gitlab (latest release tested is 14.4.1) This plugin is based on the current plugin to integrate Github with OpenProject (https://docs.openproject.org/system-admin-guide/github-integration). From 4c78ed6399cc9ce3d292d26fd5de3c4c41394272 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Thu, 27 Jan 2022 14:45:20 -0500 Subject: [PATCH 10/69] Fixes based on github plugin for v12 --- README.md | 27 ++++++++- .../git-actions-menu.component.ts | 10 ++-- .../git-actions-menu.directive.ts | 13 +++-- .../module/git-actions/git-actions.service.ts | 58 +++++++++++++------ .../module/gitlab-tab/gitlab-tab.component.ts | 10 ++-- .../gitlab-merge-request-resource.ts | 2 +- .../resources/gitlab-pipelines-resource.ts | 2 +- .../hal/resources/gitlab-user-resource.ts | 2 +- frontend/module/main.ts | 9 +-- .../merge-request/merge-request.component.ts | 4 +- .../module/tab-header/styles/tab-header.sass | 2 +- .../module/tab-header/tab-header.component.ts | 6 +- frontend/module/tab-mrs/tab-mrs.component.ts | 13 ++--- .../module/tab-mrs/wp-gitlab-mrs.service.ts | 8 +-- frontend/module/typings.d.ts | 10 ++-- openproject-gitlab_integration.gemspec | 2 +- 16 files changed, 114 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 2ebe59b4074f..34daa473d918 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.0 (pre-release) +## NEW VERSION 2.0.1 (pre-release) Based on the current Github integration (OpenProject 11 & 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). ![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) @@ -132,6 +132,16 @@ PATH openproject-webhooks ``` +Or + +``` +PATH + remote: modules/gitlab_integration + specs: + openproject-gitlab_integration (2.0.1) + openproject-webhooks +``` + Add the following in **Gemfile.modules**: ``` group :opf_plugins do @@ -141,6 +151,21 @@ group :opf_plugins do end ``` +**Update (2022-01-27):** Right now, as the plugin for Github works, it is required to add a line of code in the core of the OpenProject code before the precompilation of the assets in the following file: + +``` +frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts +``` + +Add the following just after the line defining th github resource (after line 52): + +``` + // /api/v3/(?:projectPath)/work_packages/(:workPackageId)/gitlab_merge_requests + public readonly gitlab_merge_requests = this.subResource('gitlab_merge_requests'); +``` + +This line of code is necessary so that when precompiling the plugin assets they do not generate an error. + ### OpenProject First you will need to create a user in OpenProject that will make the comments. The user will have to be added to each project with a role that allows them to comment on work packages and change status. diff --git a/frontend/module/git-actions-menu/git-actions-menu.component.ts b/frontend/module/git-actions-menu/git-actions-menu.component.ts index c0efeb16045b..80ecfe7b0f7b 100644 --- a/frontend/module/git-actions-menu/git-actions-menu.component.ts +++ b/frontend/module/git-actions-menu/git-actions-menu.component.ts @@ -29,15 +29,15 @@ import copy from 'copy-text-to-clipboard'; import { Component, Inject, Input } from '@angular/core'; -import { WorkPackageResource } from 'core-app/modules/hal/resources/work-package-resource'; -import { I18nService } from 'core-app/modules/common/i18n/i18n.service'; import { GitActionsService } from '../git-actions/git-actions.service'; -import { OPContextMenuComponent } from 'core-app/components/op-context-menu/op-context-menu.component'; +import { ISnippet } from "core-app/features/plugins/linked/openproject-gitlab_integration/typings"; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { OPContextMenuComponent } from "core-app/shared/components/op-context-menu/op-context-menu.component"; import { OpContextMenuLocalsMap, OpContextMenuLocalsToken -} from 'core-app/components/op-context-menu/op-context-menu.types'; -import { ISnippet} from "core-app/modules/plugins/linked/openproject-gitlab_integration/typings"; +} from "core-app/shared/components/op-context-menu/op-context-menu.types"; +import { I18nService } from "core-app/core/i18n/i18n.service"; @Component({ diff --git a/frontend/module/git-actions-menu/git-actions-menu.directive.ts b/frontend/module/git-actions-menu/git-actions-menu.directive.ts index 9275d0f38b76..62e54b0d74fd 100644 --- a/frontend/module/git-actions-menu/git-actions-menu.directive.ts +++ b/frontend/module/git-actions-menu/git-actions-menu.directive.ts @@ -27,12 +27,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {OpContextMenuItem} from 'core-components/op-context-menu/op-context-menu.types'; -import {OPContextMenuService} from 'core-components/op-context-menu/op-context-menu.service'; -import {Directive, ElementRef, Input} from '@angular/core'; -import {OpContextMenuTrigger} from 'core-components/op-context-menu/handlers/op-context-menu-trigger.directive'; -import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; -import {GitActionsMenuComponent} from './git-actions-menu.component'; +import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { Directive, ElementRef, Input } from '@angular/core'; +import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { GitActionsMenuComponent } from './git-actions-menu.component'; + @Directive({ selector: '[gitActionsCopyDropdown]' diff --git a/frontend/module/git-actions/git-actions.service.ts b/frontend/module/git-actions/git-actions.service.ts index fec13a499187..7a86e1a05ba3 100644 --- a/frontend/module/git-actions/git-actions.service.ts +++ b/frontend/module/git-actions/git-actions.service.ts @@ -27,28 +27,27 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import {Injectable} from '@angular/core'; -import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; -import formatter from 'tickety-tick-formatter'; +import { Injectable } from '@angular/core'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; // probably not providable in root when we want to cache the formatter and set custom templates @Injectable({ providedIn: 'root', }) export class GitActionsService { - private formatter = formatter(); - - public branchName(workPackage:WorkPackageResource):string { - return(this.formatter.branch(this.formattingInput(workPackage))); - } - - public commitMessage(workPackage:WorkPackageResource):string { - return(this.formatter.commit(this.formattingInput(workPackage))); - } - - public gitCommand(workPackage:WorkPackageResource):string { - return(this.formatter.command(this.formattingInput(workPackage))); + private sanitizeBranchString(str:string):string { + // See https://stackoverflow.com/a/3651867 for how these rules came in. + // This sanitization tries to be harsher than those rules + return str + .replace(/&/g, 'and ') // & becomes and + .replace(/ +/g, '-') // Spaces become dashes + .replace(/[\000-\039]/g, '') // ASCII control characters are out + .replace(/\177/g, '') // DEL is out + .replace(/[#\\\/\?\*\~\^\:\{\}@\.\[\]'"]/g, '') // Some other characters with special rules are out + .replace(/^[-]+/g, '') // Dashes at the start are removed + .replace(/[-]+$/g, '') // Dashes at the end are removed + .replace(/-+/g, '-') // Multiple dashes in a row are deduped + .trim(); } private formattingInput(workPackage: WorkPackageResource) { @@ -62,4 +61,29 @@ export class GitActionsService { id, type, title, url, description }); } -} \ No newline at end of file + + private sanitizeShellInput(str:string):string { + return `${str.replace(/'/g, '\\\'')}`; + } + + public branchName(workPackage:WorkPackageResource):string { + const { type, id, title } = this.formattingInput(workPackage); + return `${this.sanitizeBranchString(type)}/${id}-${this.sanitizeBranchString(title)}`.toLocaleLowerCase(); + } + + public commitMessage(workPackage:WorkPackageResource):string { + const { title, id, description, url } = this.formattingInput(workPackage); + return `[#${id}] ${title} + +${description} + +${url} +`.replace(/\n\n+/g, '\n\n'); + } + + public gitCommand(workPackage:WorkPackageResource):string { + const branch = this.branchName(workPackage); + const commit = this.commitMessage(workPackage); + return `git checkout -b '${this.sanitizeShellInput(branch)}' && git commit --allow-empty -m '${this.sanitizeShellInput(commit)}'`; + } +} diff --git a/frontend/module/gitlab-tab/gitlab-tab.component.ts b/frontend/module/gitlab-tab/gitlab-tab.component.ts index f1abea8de1f2..bcd36c1a0932 100644 --- a/frontend/module/gitlab-tab/gitlab-tab.component.ts +++ b/frontend/module/gitlab-tab/gitlab-tab.component.ts @@ -27,11 +27,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {Component, Input} from '@angular/core'; -import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; -import {PathHelperService} from 'core-app/modules/common/path-helper/path-helper.service'; -import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; -import { TabComponent } from 'core-app/components/wp-tabs/components/wp-tab-wrapper/tab'; +import { Component, Input } from '@angular/core'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { TabComponent } from "core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab"; +import { I18nService } from "core-app/core/i18n/i18n.service"; +import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; @Component({ selector: 'gitlab-tab', diff --git a/frontend/module/hal/resources/gitlab-merge-request-resource.ts b/frontend/module/hal/resources/gitlab-merge-request-resource.ts index 391d76deae6a..23163b38f0b0 100644 --- a/frontend/module/hal/resources/gitlab-merge-request-resource.ts +++ b/frontend/module/hal/resources/gitlab-merge-request-resource.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; +import { HalResource } from "core-app/features/hal/resources/hal-resource"; export class GitlabMergeRequestResource extends HalResource { public get state() { diff --git a/frontend/module/hal/resources/gitlab-pipelines-resource.ts b/frontend/module/hal/resources/gitlab-pipelines-resource.ts index 26492f5b6a8b..7e01eb4ccb67 100644 --- a/frontend/module/hal/resources/gitlab-pipelines-resource.ts +++ b/frontend/module/hal/resources/gitlab-pipelines-resource.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; +import { HalResource } from "core-app/features/hal/resources/hal-resource"; export class GitlabPipelineResource extends HalResource { public get state() { diff --git a/frontend/module/hal/resources/gitlab-user-resource.ts b/frontend/module/hal/resources/gitlab-user-resource.ts index bbcca7937ccd..3a18da80ba5d 100644 --- a/frontend/module/hal/resources/gitlab-user-resource.ts +++ b/frontend/module/hal/resources/gitlab-user-resource.ts @@ -27,7 +27,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; +import { HalResource } from "core-app/features/hal/resources/hal-resource"; export class GitlabUserResource extends HalResource { public get state() { diff --git a/frontend/module/main.ts b/frontend/module/main.ts index b2a116a9e8f8..75005fdac820 100644 --- a/frontend/module/main.ts +++ b/frontend/module/main.ts @@ -28,7 +28,10 @@ //++ import { Injector, NgModule } from '@angular/core'; -import { OpenprojectCommonModule } from 'core-app/modules/common/openproject-common.module'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; +import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service'; + import { GitlabTabComponent } from './gitlab-tab/gitlab-tab.component'; import { TabHeaderComponent } from './tab-header/tab-header.component'; @@ -37,8 +40,6 @@ import { GitActionsMenuDirective } from './git-actions-menu/git-actions-menu.dir import { GitActionsMenuComponent } from './git-actions-menu/git-actions-menu.component'; import { WorkPackagesGitlabMrsService } from './tab-mrs/wp-gitlab-mrs.service'; import { MergeRequestComponent } from './merge-request/merge-request.component'; -import { WorkPackageTabsService } from "core-components/wp-tabs/services/wp-tabs/wp-tabs.service"; -import { OpenprojectTabsModule } from "core-app/modules/common/tabs/openproject-tabs.module"; export function initializeGitlabIntegrationPlugin(injector:Injector) { const wpTabService = injector.get(WorkPackageTabsService); @@ -53,7 +54,7 @@ export function initializeGitlabIntegrationPlugin(injector:Injector) { @NgModule({ imports: [ - OpenprojectCommonModule, + OPSharedModule, OpenprojectTabsModule, ], providers: [ diff --git a/frontend/module/merge-request/merge-request.component.ts b/frontend/module/merge-request/merge-request.component.ts index beb7c0b7a987..a787b9244e69 100644 --- a/frontend/module/merge-request/merge-request.component.ts +++ b/frontend/module/merge-request/merge-request.component.ts @@ -28,10 +28,10 @@ //++ import { Component, Input } from '@angular/core'; -import {PathHelperService} from 'core-app/modules/common/path-helper/path-helper.service'; -import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; import { GitlabPipelineResource } from '../hal/resources/gitlab-pipelines-resource'; import { IGitlabMergeRequestResource } from "../../../../../../../../modules/gitlab_integration/frontend/module/typings"; +import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { I18nService } from "core-app/core/i18n/i18n.service"; @Component({ selector: 'gitlab-merge-request', diff --git a/frontend/module/tab-header/styles/tab-header.sass b/frontend/module/tab-header/styles/tab-header.sass index 16ba6d0b05b0..9e8f305eb229 100644 --- a/frontend/module/tab-header/styles/tab-header.sass +++ b/frontend/module/tab-header/styles/tab-header.sass @@ -44,4 +44,4 @@ font-weight: bold font-size: 1rem line-height: 32px - text-transform: uppercase \ No newline at end of file + text-transform: uppercase diff --git a/frontend/module/tab-header/tab-header.component.ts b/frontend/module/tab-header/tab-header.component.ts index c3ac939062a8..3be83881047c 100644 --- a/frontend/module/tab-header/tab-header.component.ts +++ b/frontend/module/tab-header/tab-header.component.ts @@ -27,9 +27,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {Component, Input} from '@angular/core'; -import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; -import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; +import { Component, Input } from '@angular/core'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { I18nService } from "core-app/core/i18n/i18n.service"; @Component({ selector: 'tab-header', diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/frontend/module/tab-mrs/tab-mrs.component.ts index e7325e4d221a..85b6cb8b844e 100644 --- a/frontend/module/tab-mrs/tab-mrs.component.ts +++ b/frontend/module/tab-mrs/tab-mrs.component.ts @@ -27,14 +27,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {Component, Input, OnInit} from '@angular/core'; -import {WorkPackageResource} from 'core-app/modules/hal/resources/work-package-resource'; -import {I18nService} from 'core-app/modules/common/i18n/i18n.service'; -import { APIV3Service } from 'core-app/modules/apiv3/api-v3.service'; -import { HalResourceService } from 'core-app/modules/hal/services/hal-resource.service'; -import { CollectionResource } from 'core-app/modules/hal/resources/collection-resource'; -import { ChangeDetectorRef } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; import { IGitlabMergeRequestResource } from "../../../../../../../../modules/gitlab_integration/frontend/module/typings"; +import { I18nService } from "core-app/core/i18n/i18n.service"; @Component({ selector: 'tab-mrs', diff --git a/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts b/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts index 915455e29078..1e17b490f0aa 100644 --- a/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts +++ b/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts @@ -27,11 +27,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from 'core-app/modules/hal/resources/work-package-resource'; -import { HalResource } from 'core-app/modules/hal/resources/hal-resource'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { HalResource } from "core-app/features/hal/resources/hal-resource"; import { Injectable } from '@angular/core'; -import { ConfigurationService } from 'core-app/modules/common/config/configuration.service'; -import { WorkPackageLinkedResourceCache } from 'core-components/wp-single-view-tabs/wp-linked-resource-cache.service'; +import { ConfigurationService } from "core-app/core/config/configuration.service"; +import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; @Injectable() export class WorkPackagesGitlabMrsService extends WorkPackageLinkedResourceCache { diff --git a/frontend/module/typings.d.ts b/frontend/module/typings.d.ts index 64ff69f93fe7..19bce8a38d59 100644 --- a/frontend/module/typings.d.ts +++ b/frontend/module/typings.d.ts @@ -27,13 +27,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TabDefinition } from "core-app/modules/common/tabs/tab.interface"; -import { HalResourceClass } from "core-app/modules/hal/resources/hal-resource"; +import { HalResourceClass } from 'core-app/modules/hal/resources/hal-resource'; + export interface ISnippet { - id: string; - name: string; - textToCopy: ()=>string + id:string; + name:string; + textToCopy:()=>string } export interface IGitlabMergeRequestResource extends HalResourceClass { diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 8ebfbe18442a..a01ae84ffe17 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.0' + s.version = '2.0.1' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From 8297f54bce3e21e7d611fad246bf1a7a98241327 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Wed, 9 Feb 2022 09:38:47 -0500 Subject: [PATCH 11/69] Better place for setup IDs of WP Status --- README.md | 145 +++++++++++++----- .../merge_request_hook.rb | 6 +- openproject-gitlab_integration.gemspec | 2 +- 3 files changed, 109 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 34daa473d918..070607b843f4 100644 --- a/README.md +++ b/README.md @@ -1,103 +1,117 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.1 (pre-release) -Based on the current Github integration (OpenProject 11 & 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). + +## NEW VERSION 2.0.2 (pre-release) + +Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). ![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) -The events captured in the WP activity log are the same as in version 1.0, only this new version includes a UI with all linked MRs, their status, their labels and the last pipeline (pending the Gitlab issue). If you use manual installation, keep in mind that it requires precompiling the assets and updating the DB with new tables. If you want to test it, do it in a test environment, and if you want, share your results in case something needs to be corrected. +The events captured in the WP activity log are the same as in version 1.0, only this new version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. + +If you use **manual** installation, keep in mind that it requires precompiling the assets and updating the DB with new tables. -The following readme file is pending review with updated documentation. +In case of a **docker** installation, you will need to build your custom docker image (see issue [#3](https://github.com/btey/openproject-gitlab-integration/issues/3)). ## Introduction -OpenProject module for integration with Gitlab (latest release tested is 14.4.1) +OpenProject module for integration with Gitlab (latest release tested is 14.7.1) This plugin is based on the current plugin to integrate Github with OpenProject (https://docs.openproject.org/system-admin-guide/github-integration). -The reference system is the same as for GitHub integration. You can use a link to the work package or just use “OP#87” in the title or description in Gitlab. +The reference system is the same as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title in Gitlab. + +> **Note about the references.** Whether or not to include the reference in certain places depends on the information that Gitlab sends through its webhook. If you include the reference in the title of an issue, the comments on the issue do not need to include the reference. The same will happen when you generate a Merge Request based on an Issue that already includes the reference; comments from that MR need not include the reference. + +#### Difference between OP and PP + +If you use `OP#` as a reference in an Issue or MR title, all comments will be replicated in OpenProject. However, sometimes you may only want to keep information about the status of an Issue/MR in OpenProject, but you don't want your comments to be published. In this case, you can use `PP#` as a reference. This way the comments will not be published in OpenProject. But if at any time one of your comments is of interest to you to be published in OpenProject you can use `OP#` *directly in that comment*. So only that comment will be published in OpenProject. The rest of the comments will remain private and will not be published. ## Available events captured in OpenProject OpenProject will **add comments** to work package for the following events: + * Merge Request (Opened, Closed and Merged) * Issue (Opened, Closed) * Push commits in Merge Requests * Comments (on Issues, Merge Request, Commits and Snippets) +* *Pipelines (pending)* OpenProject will **update WP status** in this events: + * Merge Request (opened) - Status: In progress (currently ID=7) * Merge Request (merged) - Status: Developed (currently ID=8) -> **Note about the status.** If you want to change the ID of the status you can do this in this section of the [code](https://github.com/btey/openproject-gitlab-integration/blob/58279c79035539bdd127d14e2fd148c06d85a15a/lib/open_project/gitlab_integration/notification_handlers.rb#L108-L111). -> -> **Note about the references.** Whether or not to include the reference in certain places depends on the information that Gitlab sends through its webhook. If you include the reference in the title of an issue, the comments on the issue do not need to include the reference. The same will happen when you generate a Merge Request based on an Issue that already includes the reference; comments from that MR need not include the reference. +> **Note about the status.** If you want to change the ID of the status you can do this in this section of the [code](https://github.com/btey/openproject-gitlab-integration/blob/master/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb#L40-L41). By default is *disabled*, you can enable it by setting to `true` this [lines](https://github.com/btey/openproject-gitlab-integration/blob/master/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb#L38-L39). ## Example workflow A typical workflow on Gitlab side would be: -1. **Create Issue.** +1. **Create Issue.** + - + > **Issue Opened:** Issue 6 New contact form - OP#18 for Scrum project has been opened by Administrator. 2. **Comment on issue.** - + If the reference is included in the title, the comments will not need a reference. By default, all comments will use the title as a reference. > **Commented in Issue:** Administrator commented this WP in Issue 6 New contact form - OP#18 on Scrum project: - > + > > New comment on the issue with attachment. 3. **Create Merge Request.** - + - + > **MR Opened:** Merge request 25 Draft: Resolve "New contact form - OP#18" for Scrum project has been opened by Administrator. > > **Status** changed from _Specified_ > **to** _In progress_ 4. **Comment in Merge Request.** - + - + > **Commented in MR:** Administrator commented this WP in Merge request 25 Draft: Resolve "New contact form - OP#18" on Scrum project: > > New comment on MR. 5. **Reference in other Issues or Merge Request (comments).** - + If the reference is NOT included in the title of the Issue/MR, the comments will need a reference. In OpenProject the comment will be saved as "referenced" in Issue/MR. - + - + > **Referenced in Issue:** Administrator referenced this WP in Issue 2 New backend pipeline on Scrum project: > > OP#18 New comment about... + > + > **Note:** If you use the reference `PP#` in the title of the Issue/MR, you can use `OP#` in the comment to generate the same type of comment in OpenProject. 6. **New commit in Merge Request.** - + - + > **Pushed in MR:** Administrator pushed fca3d6fb to Scrum project at 2021-03-08T08:01:57+00:00: > > Update readme.md OP#18 7. **Comment in a new commit of the Merge Request.** - + - + > **Referenced in Commit:** Administrator referenced this WP in a Commit Note 0bf0e3e9 on Scrum project: > > This change is for OP#18. 8. **Merge Request merged (generates up to 3 events).** - + - + > **Pushed in MR:** Administrator pushed 1da09cb4 to Scrum project at 2021-03-05T14:57:37+00:00: > > Merge branch '5-new-contact-form-op-18' into 'master' @@ -107,23 +121,30 @@ A typical workflow on Gitlab side would be: > Closes #6 > > See merge request root/scrum!9 - + - + > **MR Merged:** Merge request 24 Resolve "New contact form - OP#18" for Scrum project has been merged by Administrator. > > **Status** changed from _In progress_ > **to** _Developed_ - + - + > **Issue Closed:** Issue 6 New contact form - OP#18 for Scrum project has been closed by Administrator. ## Configuration -You will have to configure both OpenProject and Gitlab for the integration to work. But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenProject detects the new module. +For now, this plugin should be installed in the same place as the Github plugin that comes bundled with OpenProject. + +- **Github plugin path:** `modules/github_integration` + +- **Path to put Gitlab plugin:** `modules/gitlab_integration` + +You will have to configure both **OpenProject** and **Gitlab** for the integration to work. But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenProject detects the new module. Add the following in **Gemfile.lock**: + ``` PATH remote: modules/gitlab_integration @@ -132,17 +153,20 @@ PATH openproject-webhooks ``` -Or +> **Note:** Use version 1.0.0 if you only want to capture the events as comments. ``` PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.1) + openproject-gitlab_integration (2.0.2) openproject-webhooks ``` +> **Note:** Use version 2.0.2 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. + Add the following in **Gemfile.modules**: + ``` group :opf_plugins do ... @@ -151,13 +175,21 @@ group :opf_plugins do end ``` -**Update (2022-01-27):** Right now, as the plugin for Github works, it is required to add a line of code in the core of the OpenProject code before the precompilation of the assets in the following file: +## Changes required in the OpenProject code + +Right now, as the plugin for Github works, it is required to add some lines of code in the core of the OpenProject code before the precompilation of the assets. + +Please modify the following files: + +#### 1) api-v3-work-package-paths.ts + +Path of the file that needs to be modified: ``` frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts ``` -Add the following just after the line defining th github resource (after line 52): +Add the following just after the line defining the Github resource (after line 52): ``` // /api/v3/(?:projectPath)/work_packages/(:workPackageId)/gitlab_merge_requests @@ -166,7 +198,27 @@ Add the following just after the line defining th github resource (after line 52 This line of code is necessary so that when precompiling the plugin assets they do not generate an error. -### OpenProject +#### 2) work-package.rd + +Path of the file that needs to be modified: + +``` +app/models/work_package.rb +``` + +Add the following just after the line defining the Github table relation (after line 67): + +``` +has_and_belongs_to_many :gitlab_merge_requests +``` + +This line of code is necessary to display the content of the new Gitlab tab and not generate the error: + +``` +undefined method `gitlab_merge_requests' for # +``` + +### The Gitlab Bot user in OpenProject First you will need to create a user in OpenProject that will make the comments. The user will have to be added to each project with a role that allows them to comment on work packages and change status. @@ -178,16 +230,27 @@ Once the user is created you need to generate an OpenProject API token for it to * Click on generate in the API row. * Copy the generated key. You can now configure the necessary webhook in Gitlab. -### Gitlab +### The webhook in Gitlab In Gitlab you have to set up a webhook in each repository to be integrated with OpenProject. You need to configure just two things in the webhook: + 1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: -``` -http://openproject-url.com/webhooks/gitlab?key=ae278268 -``` -2. Enable the required triggers: Push events, Comments, Issues events, Merge request events + + ``` + http://openproject-url.com/webhooks/gitlab?key=ae278268 + ``` + +2. Enable the required triggers: + + 1. Push events + + 2. Comments + + 3. Issues events + + 4. Merge request events Now the integration is set up on both sides and you can use it. diff --git a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb index 3ac435a3eb44..c974fba83d21 100644 --- a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb @@ -37,6 +37,8 @@ class MergeRequestHook def process(payload_params) update_status_on_new_mr = false # true if you only reference one merge by work_package, else false. update_status_on_merged = false # true if you only reference one merge by work_package, else false. + wp_status_id_on_new_mr = 7 # the id of the status. + wp_status_id_on_merged = 8 # the id of the status. accepted_actions = %w[open update reopen] accepted_actions_for_comments = %w[open reopen] @@ -53,9 +55,9 @@ def process(payload_params) if (accepted_actions_for_comments.include? payload.object_attributes.action) || (accepted_states.include? payload.object_attributes.state) comment_on_referenced_work_packages(work_packages, user, notes) if payload.object_attributes.state == 'opened' && update_status_on_new_mr - status_on_referenced_work_packages(work_packages, user, 7) + status_on_referenced_work_packages(work_packages, user, wp_status_id_on_new_mr) elsif payload.object_attributes.state == 'merged' && update_status_on_merged - status_on_referenced_work_packages(work_packages, user, 8) + status_on_referenced_work_packages(work_packages, user, wp_status_id_on_merged) end end upsert_merge_request(work_packages) diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index a01ae84ffe17..755f066463e2 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.1' + s.version = '2.0.2' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From c6b753af74439d657548c85ca3ebda0622d39348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 14 Feb 2022 13:37:35 +0100 Subject: [PATCH 12/69] Remove requiring changes to the OpenProject core --- README.md | 43 ------------------- .../merge-request/merge-request.component.ts | 6 +-- frontend/module/tab-mrs/tab-mrs.component.ts | 9 ++-- lib/open_project/gitlab_integration/engine.rb | 1 + .../patches/work_package_patch.rb | 13 ++++++ 5 files changed, 22 insertions(+), 50 deletions(-) create mode 100644 lib/open_project/gitlab_integration/patches/work_package_patch.rb diff --git a/README.md b/README.md index 070607b843f4..8e57baa813af 100644 --- a/README.md +++ b/README.md @@ -175,49 +175,6 @@ group :opf_plugins do end ``` -## Changes required in the OpenProject code - -Right now, as the plugin for Github works, it is required to add some lines of code in the core of the OpenProject code before the precompilation of the assets. - -Please modify the following files: - -#### 1) api-v3-work-package-paths.ts - -Path of the file that needs to be modified: - -``` -frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts -``` - -Add the following just after the line defining the Github resource (after line 52): - -``` - // /api/v3/(?:projectPath)/work_packages/(:workPackageId)/gitlab_merge_requests - public readonly gitlab_merge_requests = this.subResource('gitlab_merge_requests'); -``` - -This line of code is necessary so that when precompiling the plugin assets they do not generate an error. - -#### 2) work-package.rd - -Path of the file that needs to be modified: - -``` -app/models/work_package.rb -``` - -Add the following just after the line defining the Github table relation (after line 67): - -``` -has_and_belongs_to_many :gitlab_merge_requests -``` - -This line of code is necessary to display the content of the new Gitlab tab and not generate the error: - -``` -undefined method `gitlab_merge_requests' for # -``` - ### The Gitlab Bot user in OpenProject First you will need to create a user in OpenProject that will make the comments. The user will have to be added to each project with a role that allows them to comment on work packages and change status. diff --git a/frontend/module/merge-request/merge-request.component.ts b/frontend/module/merge-request/merge-request.component.ts index a787b9244e69..5a341ac9908f 100644 --- a/frontend/module/merge-request/merge-request.component.ts +++ b/frontend/module/merge-request/merge-request.component.ts @@ -29,9 +29,9 @@ import { Component, Input } from '@angular/core'; import { GitlabPipelineResource } from '../hal/resources/gitlab-pipelines-resource'; -import { IGitlabMergeRequestResource } from "../../../../../../../../modules/gitlab_integration/frontend/module/typings"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import {IGitlabMergeRequestResource} from 'core-app/features/plugins/linked/openproject-gitlab_integration/typings'; @Component({ selector: 'gitlab-merge-request', diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/frontend/module/tab-mrs/tab-mrs.component.ts index 85b6cb8b844e..4700980decc5 100644 --- a/frontend/module/tab-mrs/tab-mrs.component.ts +++ b/frontend/module/tab-mrs/tab-mrs.component.ts @@ -29,11 +29,11 @@ import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { IGitlabMergeRequestResource } from "../../../../../../../../modules/gitlab_integration/frontend/module/typings"; import { I18nService } from "core-app/core/i18n/i18n.service"; +import {IGitlabMergeRequestResource} from "core-app/features/plugins/linked/openproject-gitlab_integration/typings"; +import {ApiV3Service} from "core-app/core/apiv3/api-v3.service"; @Component({ selector: 'tab-mrs', @@ -47,13 +47,14 @@ export class TabMrsComponent implements OnInit { constructor( readonly I18n:I18nService, - readonly apiV3Service:APIV3Service, + readonly apiV3Service:ApiV3Service, readonly halResourceService:HalResourceService, readonly changeDetector:ChangeDetectorRef, ) {} ngOnInit(): void { - const mergeRequestsPath = this.apiV3Service.work_packages.id({id: this.workPackage.id })?.gitlab_merge_requests.path; + const basePath = this.apiV3Service.work_packages.id(this.workPackage.id as string).path; + const mergeRequestsPath = `${basePath}/gitlab_merge_requests`; this.halResourceService .get>(mergeRequestsPath) diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index a3c16c6f699e..23010a02d1fc 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -44,6 +44,7 @@ class Engine < ::Rails::Engine :author_url => 'https://github.com/btey/openproject', bundled: true + patches %w[WorkPackage] initializer 'gitlab.register_hook' do ::OpenProject::Webhooks.register_hook 'gitlab' do |hook, environment, params, user| diff --git a/lib/open_project/gitlab_integration/patches/work_package_patch.rb b/lib/open_project/gitlab_integration/patches/work_package_patch.rb new file mode 100644 index 000000000000..eabf7ea5d07c --- /dev/null +++ b/lib/open_project/gitlab_integration/patches/work_package_patch.rb @@ -0,0 +1,13 @@ +module OpenProject::GitlabIntegration + module Patches + module WorkPackagePatch + extend ActiveSupport::Concern + + included do + has_and_belongs_to_many :gitlab_merge_requests + end + end + end +end + +::WorkPackage.include OpenProject::GitlabIntegration::Patches::WorkPackagePatch From f86cd0c623878ce8c309599618669a8defdcbf5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Tue, 15 Feb 2022 08:09:04 +0100 Subject: [PATCH 13/69] Apply suggestions from code review Co-authored-by: btey --- frontend/module/tab-mrs/tab-mrs.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/frontend/module/tab-mrs/tab-mrs.component.ts index 4700980decc5..d2b6786cfa6a 100644 --- a/frontend/module/tab-mrs/tab-mrs.component.ts +++ b/frontend/module/tab-mrs/tab-mrs.component.ts @@ -33,7 +33,7 @@ import { HalResourceService } from "core-app/features/hal/services/hal-resource. import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; import { I18nService } from "core-app/core/i18n/i18n.service"; import {IGitlabMergeRequestResource} from "core-app/features/plugins/linked/openproject-gitlab_integration/typings"; -import {ApiV3Service} from "core-app/core/apiv3/api-v3.service"; +import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; @Component({ selector: 'tab-mrs', @@ -47,7 +47,7 @@ export class TabMrsComponent implements OnInit { constructor( readonly I18n:I18nService, - readonly apiV3Service:ApiV3Service, + readonly apiV3Service:APIV3Service, readonly halResourceService:HalResourceService, readonly changeDetector:ChangeDetectorRef, ) {} From ab7070520aa6ca0539c0051dd668eb7747080750 Mon Sep 17 00:00:00 2001 From: btey Date: Tue, 15 Feb 2022 09:19:42 -0500 Subject: [PATCH 14/69] Update README.md Update version in Readme. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8e57baa813af..b7a47fb6a182 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.2 (pre-release) +## NEW VERSION 2.0.3 (pre-release) Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). @@ -159,11 +159,11 @@ PATH PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.2) + openproject-gitlab_integration (2.0.3) openproject-webhooks ``` -> **Note:** Use version 2.0.2 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. +> **Note:** Use version 2.0.3 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. Add the following in **Gemfile.modules**: From 3763aa2562afee8a2a59d2739d60650515d5e15f Mon Sep 17 00:00:00 2001 From: btey Date: Tue, 15 Feb 2022 09:24:13 -0500 Subject: [PATCH 15/69] Update README.md Set this version as stable. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7a47fb6a182..9bee5f7459b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.3 (pre-release) +## NEW VERSION 2.0.3 GA Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). From dcf73429f8d7f64f9ea6d8289ef957c0c718a186 Mon Sep 17 00:00:00 2001 From: btey Date: Tue, 15 Feb 2022 10:32:44 -0500 Subject: [PATCH 16/69] Update README.md Add the necessary dependency to Gemfile.lock which was missing in the Readme. --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 9bee5f7459b4..d55f37bb426f 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,17 @@ PATH > **Note:** Use version 2.0.3 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. +And add this other line in DEPENDENCIES section: + +``` +DEPENDENCIES +... + openproject-github_integration! + openproject-gitlab_integration! + openproject-job_status! +... +``` + Add the following in **Gemfile.modules**: ``` From fbfcfceb94430db066d16313a25af3548b736c73 Mon Sep 17 00:00:00 2001 From: btey Date: Fri, 25 Feb 2022 10:26:22 -0500 Subject: [PATCH 17/69] Updating Readme for Docker setup Updating Readme referencing official OpenProject Docker documentation --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d55f37bb426f..c9d0d75a5d42 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The events captured in the WP activity log are the same as in version 1.0, only If you use **manual** installation, keep in mind that it requires precompiling the assets and updating the DB with new tables. -In case of a **docker** installation, you will need to build your custom docker image (see issue [#3](https://github.com/btey/openproject-gitlab-integration/issues/3)). +In case of a **docker** installation, you can follow the steps described in the [OpenProject](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins) documentation. ## Introduction @@ -135,13 +135,17 @@ A typical workflow on Gitlab side would be: ## Configuration -For now, this plugin should be installed in the same place as the Github plugin that comes bundled with OpenProject. +You will have to configure both **OpenProject** and **Gitlab** for the integration to work. + +In case of **Docker** installation, follow the official OpenProject documentation [here](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins). + +In case of [**manual**](https://www.openproject.org/docs/installation-and-operations/installation/manual/) installation, this plugin should be installed in the same place as the Github plugin that comes bundled with OpenProject. - **Github plugin path:** `modules/github_integration` - **Path to put Gitlab plugin:** `modules/gitlab_integration` -You will have to configure both **OpenProject** and **Gitlab** for the integration to work. But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenProject detects the new module. +But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenProject detects the new module. Add the following in **Gemfile.lock**: @@ -186,6 +190,14 @@ group :opf_plugins do end ``` +**Note:** It's possible that you need to use these commands before and after the "bundle install" if you get an error in this step warning about a change in the Gemfile: + +``` +bundle config unset deployment +bundle install --deployment --without mysql2 sqlite development test therubyracer docker +bundle config set deployment +``` + ### The Gitlab Bot user in OpenProject First you will need to create a user in OpenProject that will make the comments. The user will have to be added to each project with a role that allows them to comment on work packages and change status. From df92416dd65ba567b3d1d17c016071bf77ce593d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Tue, 26 Apr 2022 13:32:53 +0200 Subject: [PATCH 18/69] Don't try to output link on new record --- .../patches/api/work_package_representer.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb index de310797b95e..37202bfd2d62 100644 --- a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb +++ b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb @@ -36,6 +36,8 @@ def extension ->(*) do link :gitlab, cache_if: -> { current_user.allowed_to?(:show_gitlab_content, represented.project) } do + next if represented.new_record? + { href: "#{work_package_path(id: represented.id)}/tabs/gitlab", title: "gitlab" @@ -43,6 +45,8 @@ def extension end link :gitlab_merge_requests do + next if represented.new_record? + { href: api_v3_paths.gitlab_merge_requests_by_work_package(represented.id), title: "Gitlab merge requests" From e0d2b034cddd47f99436333f81bfa9541a6c4bbd Mon Sep 17 00:00:00 2001 From: Bjoern Ludwig Date: Tue, 26 Apr 2022 17:09:53 +0200 Subject: [PATCH 19/69] Update README with current link to GitHub integration docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9d0d75a5d42..20db161d755a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ In case of a **docker** installation, you can follow the steps described in the OpenProject module for integration with Gitlab (latest release tested is 14.7.1) -This plugin is based on the current plugin to integrate Github with OpenProject (https://docs.openproject.org/system-admin-guide/github-integration). +This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). The reference system is the same as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title in Gitlab. From 23c7855686159810b72f223f6213efcd08d8fd35 Mon Sep 17 00:00:00 2001 From: btey Date: Tue, 26 Apr 2022 11:50:49 -0400 Subject: [PATCH 20/69] Updated README.md Some updates and fixes from the community. Thank you all! --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 20db161d755a..af569a6bfb1d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.3 GA +## NEW VERSION 2.0.4 GA Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). @@ -10,11 +10,13 @@ The events captured in the WP activity log are the same as in version 1.0, only If you use **manual** installation, keep in mind that it requires precompiling the assets and updating the DB with new tables. -In case of a **docker** installation, you can follow the steps described in the [OpenProject](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins) documentation. +In case of a **docker** installation, you can follow the steps described in the [OpenProject](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins) documentation (more info in [Configuration](https://github.com/btey/openproject-gitlab-integration/edit/master/README.md#configuration) section). ## Introduction -OpenProject module for integration with Gitlab (latest release tested is 14.7.1) +OpenProject module for integration with Gitlab: +* Latest Gitlab release tested: **14.10** +* Latest OpenProject release tested: **12.0.10** This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). @@ -24,7 +26,7 @@ The reference system is the same as for GitHub integration. You can use a link t #### Difference between OP and PP -If you use `OP#` as a reference in an Issue or MR title, all comments will be replicated in OpenProject. However, sometimes you may only want to keep information about the status of an Issue/MR in OpenProject, but you don't want your comments to be published. In this case, you can use `PP#` as a reference. This way the comments will not be published in OpenProject. But if at any time one of your comments is of interest to you to be published in OpenProject you can use `OP#` *directly in that comment*. So only that comment will be published in OpenProject. The rest of the comments will remain private and will not be published. +If you use `OP#` as a reference in an Issue or MR title, all comments will be replicated in OpenProject. However, sometimes you may only want to keep information about the status of an Issue/MR in OpenProject, but you don't want your comments to be published. In this case, you can use `PP#` as a reference. This way the comments will not be published in OpenProject. But if at any time one of your comments in a private Issue/MR is of interest to you to be published in OpenProject you can use `OP#` *directly in that comment*. So only that comment will be published in OpenProject. The rest of the comments will remain private and will not be published. ## Available events captured in OpenProject @@ -137,7 +139,12 @@ A typical workflow on Gitlab side would be: You will have to configure both **OpenProject** and **Gitlab** for the integration to work. -In case of **Docker** installation, follow the official OpenProject documentation [here](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins). +In case of **Docker** installation, follow the official OpenProject documentation [here](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins). If for some reason the installation with Docker described in the official documentation does not work for you, you can try building your own docker image: +* Clone from the Openproject Repo: `git clone https://github.com/opf/openproject.git --branch=stable/12 --depth=1 .` +* Clone the plugin inside the modules folder: `git clone https://github.com/btey/openproject-gitlab-integration.git --depth=1 modules/gitlab_integration` +* Apply the changes below in Gemfile.lock and Gemfile.modules (the same ones you would do in a manual install). +* Build the container: `docker build -t openproject-docker --file=docker/prod/Dockerfile .` +* Now run the image following the official documentation In case of [**manual**](https://www.openproject.org/docs/installation-and-operations/installation/manual/) installation, this plugin should be installed in the same place as the Github plugin that comes bundled with OpenProject. @@ -163,11 +170,11 @@ PATH PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.3) + openproject-gitlab_integration (2.0.4) openproject-webhooks ``` -> **Note:** Use version 2.0.3 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. +> **Note:** Use version 2.0.4 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. And add this other line in DEPENDENCIES section: @@ -234,6 +241,8 @@ You need to configure just two things in the webhook: Now the integration is set up on both sides and you can use it. +> **Note:** If you are installing and configuring OpenProject on the same server as Gitlab you will need to enable in Gitlab the option "Allow requests to the local network from web hooks and services" so that it can send the data locally to the OpenProject webhook since they will be on the same machine. + ## How to report bugs or issues Any error, bug or issue can be reported by creating a new [issue](https://github.com/btey/openproject-gitlab-integration/issues/new). From 9139898b4a8075e1cbdc694758a9eff97952b169 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Wed, 4 May 2022 13:43:04 -0400 Subject: [PATCH 21/69] Fix compatibility with OP permission arguments --- lib/open_project/gitlab_integration/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 23010a02d1fc..78ac422fe7ed 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -68,7 +68,7 @@ class Engine < ::Rails::Engine initializer 'gitlab.permissions' do OpenProject::AccessControl.map do |ac_map| ac_map.project_module(:gitlab, dependencies: :work_package_tracking) do |pm_map| - pm_map.permission(:show_gitlab_content, {}, {}) + pm_map.permission(:show_gitlab_content, {}) end end end From 1090e206b36c303ad45afbfa8a6e850ee44174b1 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Wed, 4 May 2022 13:52:15 -0400 Subject: [PATCH 22/69] Fix APIV3Service library name change --- frontend/module/tab-mrs/tab-mrs.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/frontend/module/tab-mrs/tab-mrs.component.ts index d2b6786cfa6a..4700980decc5 100644 --- a/frontend/module/tab-mrs/tab-mrs.component.ts +++ b/frontend/module/tab-mrs/tab-mrs.component.ts @@ -33,7 +33,7 @@ import { HalResourceService } from "core-app/features/hal/services/hal-resource. import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; import { I18nService } from "core-app/core/i18n/i18n.service"; import {IGitlabMergeRequestResource} from "core-app/features/plugins/linked/openproject-gitlab_integration/typings"; -import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; +import {ApiV3Service} from "core-app/core/apiv3/api-v3.service"; @Component({ selector: 'tab-mrs', @@ -47,7 +47,7 @@ export class TabMrsComponent implements OnInit { constructor( readonly I18n:I18nService, - readonly apiV3Service:APIV3Service, + readonly apiV3Service:ApiV3Service, readonly halResourceService:HalResourceService, readonly changeDetector:ChangeDetectorRef, ) {} From f9f6348d450a2f91a0e2f3b6b72a0de917cf8298 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Wed, 4 May 2022 14:05:44 -0400 Subject: [PATCH 23/69] Update readme and increment version --- README.md | 8 ++++---- openproject-gitlab_integration.gemspec | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index af569a6bfb1d..9a1d8fd62442 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.4 GA +## NEW VERSION 2.0.5 GA Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). @@ -16,7 +16,7 @@ In case of a **docker** installation, you can follow the steps described in the OpenProject module for integration with Gitlab: * Latest Gitlab release tested: **14.10** -* Latest OpenProject release tested: **12.0.10** +* Latest OpenProject release tested: **12.1.0** (for previous versions of OpenProject use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). @@ -170,11 +170,11 @@ PATH PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.4) + openproject-gitlab_integration (2.0.5) openproject-webhooks ``` -> **Note:** Use version 2.0.4 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. +> **Note:** Use version 2.0.5 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. And add this other line in DEPENDENCIES section: diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 755f066463e2..0ac301a340a5 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.2' + s.version = '2.0.5' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From 9a6087321043abd21a026ec9e4b80ae3812b55b0 Mon Sep 17 00:00:00 2001 From: btey Date: Mon, 6 Jun 2022 13:23:06 -0400 Subject: [PATCH 24/69] Update README.md Added a link for DEB/RPM installations to the official OpenProject docs. --- README.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9a1d8fd62442..912e58617337 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ In case of a **docker** installation, you can follow the steps described in the ## Introduction OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **14.10** -* Latest OpenProject release tested: **12.1.0** (for previous versions of OpenProject use v2.0.4) +* Latest Gitlab release tested: **15.0** +* Latest OpenProject release tested: **12.1.4** (for OpenProject versions earlier than 12.1.0 use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). @@ -146,6 +146,8 @@ In case of **Docker** installation, follow the official OpenProject documentatio * Build the container: `docker build -t openproject-docker --file=docker/prod/Dockerfile .` * Now run the image following the official documentation +In case of **DEB/RPM** based instalation, follow the official OpenProject documentation [here](https://www.openproject.org/docs/installation-and-operations/configuration/plugins/). + In case of [**manual**](https://www.openproject.org/docs/installation-and-operations/installation/manual/) installation, this plugin should be installed in the same place as the Github plugin that comes bundled with OpenProject. - **Github plugin path:** `modules/github_integration` @@ -156,16 +158,6 @@ But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenP Add the following in **Gemfile.lock**: -``` -PATH - remote: modules/gitlab_integration - specs: - openproject-gitlab_integration (1.0.0) - openproject-webhooks -``` - -> **Note:** Use version 1.0.0 if you only want to capture the events as comments. - ``` PATH remote: modules/gitlab_integration @@ -174,8 +166,6 @@ PATH openproject-webhooks ``` -> **Note:** Use version 2.0.5 if you want to capture the events as comments and see the new UI Tab with the linked Merge Requests. - And add this other line in DEPENDENCIES section: ``` From 9aca6a8a82537149873853cede2d911c49dcd6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Tue, 23 Aug 2022 09:36:03 +0200 Subject: [PATCH 25/69] Use built-in mechanism to register module and permission --- lib/open_project/gitlab_integration/engine.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 78ac422fe7ed..2c47bb262ff5 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -42,7 +42,11 @@ class Engine < ::Rails::Engine register 'openproject-gitlab_integration', :author_url => 'https://github.com/btey/openproject', - bundled: true + bundled: true do + project_module(:gitlab, dependencies: :work_package_tracking) do + permission(:show_gitlab_content, {}) + end + end patches %w[WorkPackage] @@ -65,14 +69,6 @@ class Engine < ::Rails::Engine &NotificationHandlers.method(:pipeline_hook)) end - initializer 'gitlab.permissions' do - OpenProject::AccessControl.map do |ac_map| - ac_map.project_module(:gitlab, dependencies: :work_package_tracking) do |pm_map| - pm_map.permission(:show_gitlab_content, {}) - end - end - end - extend_api_response(:v3, :work_packages, :work_package, &::OpenProject::GitlabIntegration::Patches::API::WorkPackageRepresenter.extension) From b26bec1c68d29319e92bb17e68798b42dc6daa69 Mon Sep 17 00:00:00 2001 From: btey Date: Tue, 23 Aug 2022 08:20:25 -0400 Subject: [PATCH 26/69] Update README.md New version 2.0.6 --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 912e58617337..21d9fe0b6d44 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,17 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.5 GA +## NEW VERSION 2.0.6 GA Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). ![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) -The events captured in the WP activity log are the same as in version 1.0, only this new version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. - -If you use **manual** installation, keep in mind that it requires precompiling the assets and updating the DB with new tables. - -In case of a **docker** installation, you can follow the steps described in the [OpenProject](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins) documentation (more info in [Configuration](https://github.com/btey/openproject-gitlab-integration/edit/master/README.md#configuration) section). - +This 2.x version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. ## Introduction OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **15.0** -* Latest OpenProject release tested: **12.1.4** (for OpenProject versions earlier than 12.1.0 use v2.0.4) +* Latest Gitlab release tested: **15.3.1** +* Latest OpenProject release tested: **12.2.1** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). @@ -144,7 +139,7 @@ In case of **Docker** installation, follow the official OpenProject documentatio * Clone the plugin inside the modules folder: `git clone https://github.com/btey/openproject-gitlab-integration.git --depth=1 modules/gitlab_integration` * Apply the changes below in Gemfile.lock and Gemfile.modules (the same ones you would do in a manual install). * Build the container: `docker build -t openproject-docker --file=docker/prod/Dockerfile .` -* Now run the image following the official documentation +* Now run the image following the official documentation. In case of **DEB/RPM** based instalation, follow the official OpenProject documentation [here](https://www.openproject.org/docs/installation-and-operations/configuration/plugins/). @@ -162,7 +157,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.5) + openproject-gitlab_integration (2.0.6) openproject-webhooks ``` From 0ea618977dc8d200798762913985284b54c52917 Mon Sep 17 00:00:00 2001 From: btey Date: Wed, 7 Dec 2022 09:38:01 -0500 Subject: [PATCH 27/69] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21d9fe0b6d44..beb86530537c 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ This 2.x version includes a UI with all linked MRs, their status, their labels a ## Introduction OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **15.3.1** -* Latest OpenProject release tested: **12.2.1** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) +* Latest Gitlab release tested: **15.6.2** +* Latest OpenProject release tested: **12.4.0** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). From a9b4b6fb0b0f9da4576417efbcf95e208b7a5412 Mon Sep 17 00:00:00 2001 From: wagner-petrosoft <116894938+wagner-petrosoft@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:26:51 -0300 Subject: [PATCH 28/69] Update README.md Explicit that the key is the generated access token --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index beb86530537c..34e93ee359db 100644 --- a/README.md +++ b/README.md @@ -208,10 +208,10 @@ In Gitlab you have to set up a webhook in each repository to be integrated with You need to configure just two things in the webhook: -1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: +1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key with previouslly generated token as value. In the end the URL should look something like this: ``` - http://openproject-url.com/webhooks/gitlab?key=ae278268 + http://openproject-url.com/webhooks/gitlab?key=generated_access_token ``` 2. Enable the required triggers: From f8e11f9fc278783990324eca4b09ab1bbd70ac6e Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Wed, 22 Feb 2023 17:08:41 -0500 Subject: [PATCH 29/69] New colors and style based on current gitlab version --- README.md | 4 ++-- .../merge-request.component.sass | 24 +++++++++++-------- .../merge-request/merge-request.component.ts | 2 +- .../module/tab-header/styles/tab-header.sass | 1 - openproject-gitlab_integration.gemspec | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 34e93ee359db..6d838bd5ae09 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.6 GA +## NEW VERSION 2.0.7 GA Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). -![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) + This 2.x version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. ## Introduction diff --git a/frontend/module/merge-request/merge-request.component.sass b/frontend/module/merge-request/merge-request.component.sass index f29d25befc9f..c7d2a6422590 100644 --- a/frontend/module/merge-request/merge-request.component.sass +++ b/frontend/module/merge-request/merge-request.component.sass @@ -91,25 +91,29 @@ &--state grid-area: state display: inline-block - padding: 7px 11px - border-radius: 5px - border: 1px solid #fff + padding: 6px 10px + border-radius: 10rem + border: 3px solid #fff color: #fff - font-size: 0.9rem - font-weight: 700 + font-size: 0.8rem + font-weight: 600 text-transform: capitalize - &_draft - background-color: #6a737d + &_open + background-color: #c3e6cd + color: #24663b &_ready - background-color: #108548 + background-color: #c3e6cd + color: #24663b &_merged - background-color: #1f75cb + background-color: #cbe2f9 + color: #0b5cad &_closed - background-color: #dd2b0e + background-color: #fdd4cd + color: #ae1800 &--pipeline-label grid-area: pipeline-label diff --git a/frontend/module/merge-request/merge-request.component.ts b/frontend/module/merge-request/merge-request.component.ts index 5a341ac9908f..bbd469e442f8 100644 --- a/frontend/module/merge-request/merge-request.component.ts +++ b/frontend/module/merge-request/merge-request.component.ts @@ -60,7 +60,7 @@ export class MergeRequestComponent { get state() { if (this.mergeRequest.state === 'opened') { - return (this.mergeRequest.draft ? 'draft' : 'ready'); + return (this.mergeRequest.draft ? 'open' : 'ready'); } else { return(this.mergeRequest.merged ? 'merged' : 'closed'); } diff --git a/frontend/module/tab-header/styles/tab-header.sass b/frontend/module/tab-header/styles/tab-header.sass index 9e8f305eb229..f8c45ecd06fa 100644 --- a/frontend/module/tab-header/styles/tab-header.sass +++ b/frontend/module/tab-header/styles/tab-header.sass @@ -34,7 +34,6 @@ border-bottom: 1px solid #ddd - margin: 1.5rem 0 0.8rem 0 .title flex: 1 1 auto diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 0ac301a340a5..8f1f0a9a5168 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.5' + s.version = '2.0.7' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From 1009022f5958456b4cc3c3ada9e5a640e2192722 Mon Sep 17 00:00:00 2001 From: btey Date: Wed, 22 Feb 2023 17:13:37 -0500 Subject: [PATCH 30/69] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d838bd5ae09..3424617c8f16 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ This 2.x version includes a UI with all linked MRs, their status, their labels a ## Introduction OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **15.6.2** -* Latest OpenProject release tested: **12.4.0** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) +* Latest Gitlab release tested: **15.8.3** +* Latest OpenProject release tested: **12.4.4** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). From 339050aad92a042dd7003b274759caf1660d02c3 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Tue, 14 Mar 2023 14:15:04 -0400 Subject: [PATCH 31/69] Counter in gitlab tab --- README.md | 8 ++++---- app/models/gitlab_merge_request.rb | 2 +- app/models/gitlab_pipeline.rb | 2 +- app/models/gitlab_user.rb | 2 +- app/workers/cron/clear_old_merge_requests_job.rb | 2 +- config/locales/crowdin/ar.yml | 2 +- config/locales/crowdin/bg.yml | 2 +- config/locales/crowdin/ca.yml | 2 +- config/locales/crowdin/cs.yml | 2 +- config/locales/crowdin/da.yml | 2 +- config/locales/crowdin/de.yml | 2 +- config/locales/crowdin/el.yml | 2 +- config/locales/crowdin/es.yml | 2 +- config/locales/crowdin/fi.yml | 2 +- config/locales/crowdin/fil.yml | 2 +- config/locales/crowdin/fr.yml | 2 +- config/locales/crowdin/hr.yml | 2 +- config/locales/crowdin/hu.yml | 2 +- config/locales/crowdin/id.yml | 2 +- config/locales/crowdin/it.yml | 2 +- config/locales/crowdin/ja.yml | 2 +- config/locales/crowdin/js-ar.yml | 2 +- config/locales/crowdin/js-bg.yml | 2 +- config/locales/crowdin/js-ca.yml | 2 +- config/locales/crowdin/js-cs.yml | 2 +- config/locales/crowdin/js-da.yml | 2 +- config/locales/crowdin/js-de.yml | 2 +- config/locales/crowdin/js-el.yml | 2 +- config/locales/crowdin/js-es.yml | 2 +- config/locales/crowdin/js-fi.yml | 2 +- config/locales/crowdin/js-fil.yml | 2 +- config/locales/crowdin/js-fr.yml | 2 +- config/locales/crowdin/js-hr.yml | 2 +- config/locales/crowdin/js-hu.yml | 2 +- config/locales/crowdin/js-id.yml | 2 +- config/locales/crowdin/js-it.yml | 2 +- config/locales/crowdin/js-ja.yml | 2 +- config/locales/crowdin/js-ko.yml | 2 +- config/locales/crowdin/js-lt.yml | 2 +- config/locales/crowdin/js-nl.yml | 2 +- config/locales/crowdin/js-no.yml | 2 +- config/locales/crowdin/js-pl.yml | 2 +- config/locales/crowdin/js-pt.yml | 2 +- config/locales/crowdin/js-ro.yml | 2 +- config/locales/crowdin/js-ru.yml | 2 +- config/locales/crowdin/js-sk.yml | 2 +- config/locales/crowdin/js-sl.yml | 2 +- config/locales/crowdin/js-sv.yml | 2 +- config/locales/crowdin/js-tr.yml | 2 +- config/locales/crowdin/js-uk.yml | 2 +- config/locales/crowdin/js-vi.yml | 2 +- config/locales/crowdin/js-zh-CN.yml | 2 +- config/locales/crowdin/js-zh-TW.yml | 2 +- config/locales/crowdin/ko.yml | 2 +- config/locales/crowdin/lt.yml | 2 +- config/locales/crowdin/nl.yml | 2 +- config/locales/crowdin/no.yml | 2 +- config/locales/crowdin/pl.yml | 2 +- config/locales/crowdin/pt.yml | 2 +- config/locales/crowdin/ro.yml | 2 +- config/locales/crowdin/ru.yml | 2 +- config/locales/crowdin/sk.yml | 2 +- config/locales/crowdin/sl.yml | 2 +- config/locales/crowdin/sv.yml | 2 +- config/locales/crowdin/tr.yml | 2 +- config/locales/crowdin/uk.yml | 2 +- config/locales/crowdin/vi.yml | 2 +- config/locales/crowdin/zh-CN.yml | 2 +- config/locales/crowdin/zh-TW.yml | 2 +- config/locales/de.yml | 2 +- config/locales/en.yml | 2 +- config/locales/js-en.yml | 2 +- .../20211015110000_gitlab_integration_models.rb | 2 +- frontend/module/main.ts | 16 ++++++++++++++++ ...itlab_merge_request_collection_representer.rb | 2 +- .../gitlab_merge_request_representer.rb | 2 +- .../gitlab_merge_requests_by_work_package_api.rb | 2 +- .../gitlab_pipeline_representer.rb | 2 +- .../gitlab_user_representer.rb | 2 +- lib/open_project/gitlab_integration.rb | 2 +- lib/open_project/gitlab_integration/engine.rb | 2 +- .../gitlab_integration/hook_handler.rb | 2 +- .../notification_handler/helper.rb | 2 +- .../notification_handler/issue_hook.rb | 2 +- .../notification_handler/merge_request_hook.rb | 2 +- .../notification_handler/note_hook.rb | 2 +- .../notification_handler/pipeline_hook.rb | 2 +- .../notification_handler/push_hook.rb | 2 +- .../gitlab_integration/notification_handlers.rb | 2 +- .../patches/api/work_package_representer.rb | 2 +- lib/open_project/gitlab_integration/services.rb | 2 +- lib/openproject-gitlab_integration.rb | 2 +- openproject-gitlab_integration.gemspec | 4 ++-- 93 files changed, 112 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index 3424617c8f16..28c0328aadd6 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.7 GA +## NEW VERSION 2.0.8 GA Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). - +preview This 2.x version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. ## Introduction OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **15.8.3** -* Latest OpenProject release tested: **12.4.4** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) +* Latest Gitlab release tested: **15.9** +* Latest OpenProject release tested: **12.4.5** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). diff --git a/app/models/gitlab_merge_request.rb b/app/models/gitlab_merge_request.rb index 6218467d5e39..ca99e18eb59a 100644 --- a/app/models/gitlab_merge_request.rb +++ b/app/models/gitlab_merge_request.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/app/models/gitlab_pipeline.rb b/app/models/gitlab_pipeline.rb index 338899c757c7..4433ad3e8409 100644 --- a/app/models/gitlab_pipeline.rb +++ b/app/models/gitlab_pipeline.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/app/models/gitlab_user.rb b/app/models/gitlab_user.rb index 31d4212bcffb..1e0015824da7 100644 --- a/app/models/gitlab_user.rb +++ b/app/models/gitlab_user.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/app/workers/cron/clear_old_merge_requests_job.rb b/app/workers/cron/clear_old_merge_requests_job.rb index 80f85238ea6a..ce3806f326dc 100644 --- a/app/workers/cron/clear_old_merge_requests_job.rb +++ b/app/workers/cron/clear_old_merge_requests_job.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index d89f971c8b4a..4640bb302244 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 02fd62659447..ac16b593442b 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index bf38da46bea1..7c4b926621bb 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index 8cddda63ec4a..5f7350af23d0 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index b92d366080fd..f275e1d3ae93 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index fdec7b9cc12e..7b9219629559 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index 32ece46687e8..f98c9606f96b 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index b241daab87f3..964afa0aeb38 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index 96db43ec7d08..c6c1df440e7b 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index 8f3c45b2f193..b2a456b5c216 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 64d8c8a87f79..fef1f113818c 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index c242ff118e52..308f94c45cd7 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index cd0a5322b376..a95e80575304 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index fee8c289aba1..bb4954d3c5ef 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 1958ba0a681e..a10cdf8223a1 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index d1d0067e71ab..cd098bdacdc9 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-ar.yml b/config/locales/crowdin/js-ar.yml index 80acef0cbbc9..3e2028a64cbf 100644 --- a/config/locales/crowdin/js-ar.yml +++ b/config/locales/crowdin/js-ar.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-bg.yml b/config/locales/crowdin/js-bg.yml index 97ab6ef8f384..3cd8fa468f3e 100644 --- a/config/locales/crowdin/js-bg.yml +++ b/config/locales/crowdin/js-bg.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-ca.yml b/config/locales/crowdin/js-ca.yml index 0b6d059dfc5b..c0204c42e067 100644 --- a/config/locales/crowdin/js-ca.yml +++ b/config/locales/crowdin/js-ca.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-cs.yml b/config/locales/crowdin/js-cs.yml index 650fb0fb19f6..a5d495518c2c 100644 --- a/config/locales/crowdin/js-cs.yml +++ b/config/locales/crowdin/js-cs.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-da.yml b/config/locales/crowdin/js-da.yml index 5a0dfb4166cc..eac4cb5ec5cd 100644 --- a/config/locales/crowdin/js-da.yml +++ b/config/locales/crowdin/js-da.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-de.yml b/config/locales/crowdin/js-de.yml index 7f977cf9e04a..b194a00b07c3 100644 --- a/config/locales/crowdin/js-de.yml +++ b/config/locales/crowdin/js-de.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-el.yml b/config/locales/crowdin/js-el.yml index 7f137d133e56..cb195d5a8e68 100644 --- a/config/locales/crowdin/js-el.yml +++ b/config/locales/crowdin/js-el.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-es.yml b/config/locales/crowdin/js-es.yml index 7197c175e9dd..bcd3948614fa 100644 --- a/config/locales/crowdin/js-es.yml +++ b/config/locales/crowdin/js-es.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-fi.yml b/config/locales/crowdin/js-fi.yml index bbeb52128acf..cc7472c9b9b8 100644 --- a/config/locales/crowdin/js-fi.yml +++ b/config/locales/crowdin/js-fi.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-fil.yml b/config/locales/crowdin/js-fil.yml index cfc2447a0536..8ec030ecc1d4 100644 --- a/config/locales/crowdin/js-fil.yml +++ b/config/locales/crowdin/js-fil.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-fr.yml b/config/locales/crowdin/js-fr.yml index 92c71735c982..1a8b5fa42d92 100644 --- a/config/locales/crowdin/js-fr.yml +++ b/config/locales/crowdin/js-fr.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-hr.yml b/config/locales/crowdin/js-hr.yml index 68da7f355d4e..4a7ba75de548 100644 --- a/config/locales/crowdin/js-hr.yml +++ b/config/locales/crowdin/js-hr.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-hu.yml b/config/locales/crowdin/js-hu.yml index 42d437c163c5..1dc122fadd7d 100644 --- a/config/locales/crowdin/js-hu.yml +++ b/config/locales/crowdin/js-hu.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-id.yml b/config/locales/crowdin/js-id.yml index b8b027240f00..87355960b03f 100644 --- a/config/locales/crowdin/js-id.yml +++ b/config/locales/crowdin/js-id.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-it.yml b/config/locales/crowdin/js-it.yml index 6b3fc0e79125..c8a86bffd993 100644 --- a/config/locales/crowdin/js-it.yml +++ b/config/locales/crowdin/js-it.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-ja.yml b/config/locales/crowdin/js-ja.yml index ada7bc1dba95..153e3ef8a0d5 100644 --- a/config/locales/crowdin/js-ja.yml +++ b/config/locales/crowdin/js-ja.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-ko.yml b/config/locales/crowdin/js-ko.yml index bf9ca57c6975..183450ed5cf5 100644 --- a/config/locales/crowdin/js-ko.yml +++ b/config/locales/crowdin/js-ko.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-lt.yml b/config/locales/crowdin/js-lt.yml index 730001aae8df..8e71278d5799 100644 --- a/config/locales/crowdin/js-lt.yml +++ b/config/locales/crowdin/js-lt.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-nl.yml b/config/locales/crowdin/js-nl.yml index f92a3b9881f0..ff8213d5df64 100644 --- a/config/locales/crowdin/js-nl.yml +++ b/config/locales/crowdin/js-nl.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-no.yml b/config/locales/crowdin/js-no.yml index d59db3b7e2a9..e5e6a941c5ed 100644 --- a/config/locales/crowdin/js-no.yml +++ b/config/locales/crowdin/js-no.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-pl.yml b/config/locales/crowdin/js-pl.yml index e3122e06816c..43b90e423664 100644 --- a/config/locales/crowdin/js-pl.yml +++ b/config/locales/crowdin/js-pl.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-pt.yml b/config/locales/crowdin/js-pt.yml index 58a97e0cb3a3..e61df0b657b5 100644 --- a/config/locales/crowdin/js-pt.yml +++ b/config/locales/crowdin/js-pt.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-ro.yml b/config/locales/crowdin/js-ro.yml index e594199bc251..9e134ca512de 100644 --- a/config/locales/crowdin/js-ro.yml +++ b/config/locales/crowdin/js-ro.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index 44566acea887..a51af8892daf 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-sk.yml b/config/locales/crowdin/js-sk.yml index 88bb174b562c..5e52f2533eab 100644 --- a/config/locales/crowdin/js-sk.yml +++ b/config/locales/crowdin/js-sk.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-sl.yml b/config/locales/crowdin/js-sl.yml index 85f50aedc827..fa126147b637 100644 --- a/config/locales/crowdin/js-sl.yml +++ b/config/locales/crowdin/js-sl.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-sv.yml b/config/locales/crowdin/js-sv.yml index 25946481aa20..589944e75275 100644 --- a/config/locales/crowdin/js-sv.yml +++ b/config/locales/crowdin/js-sv.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-tr.yml b/config/locales/crowdin/js-tr.yml index 4ce717f2e344..1d2bff4723b2 100644 --- a/config/locales/crowdin/js-tr.yml +++ b/config/locales/crowdin/js-tr.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-uk.yml b/config/locales/crowdin/js-uk.yml index 29c081cf91cd..a594075b4278 100644 --- a/config/locales/crowdin/js-uk.yml +++ b/config/locales/crowdin/js-uk.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-vi.yml b/config/locales/crowdin/js-vi.yml index 84e2c7fa5a1e..96ff09725bb2 100644 --- a/config/locales/crowdin/js-vi.yml +++ b/config/locales/crowdin/js-vi.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-zh-CN.yml b/config/locales/crowdin/js-zh-CN.yml index fdcec3786265..356106c814d3 100644 --- a/config/locales/crowdin/js-zh-CN.yml +++ b/config/locales/crowdin/js-zh-CN.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/js-zh-TW.yml b/config/locales/crowdin/js-zh-TW.yml index 6cdf89f89a98..f25cb2161f9c 100644 --- a/config/locales/crowdin/js-zh-TW.yml +++ b/config/locales/crowdin/js-zh-TW.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index ec4ee1f661e5..cff63d5f4b2c 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index ba52cf949c9e..1fe1bc3535bc 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index bb25bfdaa700..2af08592ddc6 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index 7f32b8b23e91..37d905e95408 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index 53c2816779fe..1be5da1f1162 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/pt.yml b/config/locales/crowdin/pt.yml index 211a21525f41..473327d92560 100644 --- a/config/locales/crowdin/pt.yml +++ b/config/locales/crowdin/pt.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index d26476323cd4..2032891af2d9 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 11730a838449..b17b09dc0d2b 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index fd444881cddc..424d97842e61 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index bc460be9ed94..e25178e640a9 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index 10baf1b2fca5..34fed669dcd9 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index 12d07288b188..0c8fd994f742 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index 8e2ae9a931ee..039c8ca03e44 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index a8b763826baf..0f293629c13b 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml index 14265438a7d0..57a96873f18d 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index c3db200526fb..2361760b3f2c 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/de.yml b/config/locales/de.yml index ef813ecb454d..af758b73f718 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/en.yml b/config/locales/en.yml index f31ceded9587..76dbbd1af51f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 5e3d38f67caf..dd2d881a9197 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/db/migrate/20211015110000_gitlab_integration_models.rb b/db/migrate/20211015110000_gitlab_integration_models.rb index bf1f58ce8e6f..587a72e946da 100644 --- a/db/migrate/20211015110000_gitlab_integration_models.rb +++ b/db/migrate/20211015110000_gitlab_integration_models.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/main.ts b/frontend/module/main.ts index 75005fdac820..7435e976b80d 100644 --- a/frontend/module/main.ts +++ b/frontend/module/main.ts @@ -40,6 +40,21 @@ import { GitActionsMenuDirective } from './git-actions-menu/git-actions-menu.dir import { GitActionsMenuComponent } from './git-actions-menu/git-actions-menu.component'; import { WorkPackagesGitlabMrsService } from './tab-mrs/wp-gitlab-mrs.service'; import { MergeRequestComponent } from './merge-request/merge-request.component'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +export function workPackageGitlabMrsCount( + workPackage:WorkPackageResource, + injector:Injector, +):Observable { + const gitlabMrsService = injector.get(WorkPackagesGitlabMrsService); + return gitlabMrsService + .requireAndStream(workPackage) + .pipe( + map((mrs) => mrs.length), + ); +} export function initializeGitlabIntegrationPlugin(injector:Injector) { const wpTabService = injector.get(WorkPackageTabsService); @@ -48,6 +63,7 @@ export function initializeGitlabIntegrationPlugin(injector:Injector) { name: I18n.t('js.gitlab_integration.work_packages.tab_name'), id: 'gitlab', displayable: (workPackage) => !!workPackage.gitlab, + count: workPackageGitlabMrsCount, }); } diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb index b643c0267317..503649142cc3 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb index f1cce2d2cdfe..fa08a2b1a4de 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb index 28c3920c258f..3976818491b8 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb index c1ec18dd818d..0236f90a934d 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb index 914fce5146d2..55f90e5c6416 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration.rb b/lib/open_project/gitlab_integration.rb index c524473ec832..70f358282d54 100644 --- a/lib/open_project/gitlab_integration.rb +++ b/lib/open_project/gitlab_integration.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 2c47bb262ff5..08da552a24b3 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/hook_handler.rb b/lib/open_project/gitlab_integration/hook_handler.rb index fc163e17ea0f..0476494c5447 100644 --- a/lib/open_project/gitlab_integration/hook_handler.rb +++ b/lib/open_project/gitlab_integration/hook_handler.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handler/helper.rb b/lib/open_project/gitlab_integration/notification_handler/helper.rb index dec15c25dad3..396ff1e35679 100644 --- a/lib/open_project/gitlab_integration/notification_handler/helper.rb +++ b/lib/open_project/gitlab_integration/notification_handler/helper.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb b/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb index 8688db056002..8f41dfdc2585 100644 --- a/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb index c974fba83d21..fff9432bee6a 100644 --- a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb index 049998d40ad5..0964aee7eec6 100644 --- a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb index 85cdec1ebfaa..baf7ca8db2b4 100644 --- a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handler/push_hook.rb b/lib/open_project/gitlab_integration/notification_handler/push_hook.rb index 83f9af261136..3c67dd3ad00b 100644 --- a/lib/open_project/gitlab_integration/notification_handler/push_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/push_hook.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/lib/open_project/gitlab_integration/notification_handlers.rb index 9049ca6deb0a..4acd6c6c0724 100644 --- a/lib/open_project/gitlab_integration/notification_handlers.rb +++ b/lib/open_project/gitlab_integration/notification_handlers.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb index 37202bfd2d62..7a67f36f5d55 100644 --- a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb +++ b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/open_project/gitlab_integration/services.rb b/lib/open_project/gitlab_integration/services.rb index 25f735aabe71..adce10305094 100644 --- a/lib/open_project/gitlab_integration/services.rb +++ b/lib/open_project/gitlab_integration/services.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/lib/openproject-gitlab_integration.rb b/lib/openproject-gitlab_integration.rb index c4d802aea228..146bd94ac4ef 100644 --- a/lib/openproject-gitlab_integration.rb +++ b/lib/openproject-gitlab_integration.rb @@ -1,6 +1,6 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 8f1f0a9a5168..3f2fdc68cc0a 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -2,7 +2,7 @@ # #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2021 Ben Tey +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.7' + s.version = '2.0.8' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From 004349a81e207c7cc8682fe91feaeade0ba03496 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Fri, 24 Mar 2023 08:55:27 -0400 Subject: [PATCH 32/69] OPSharedModule renamed to OpSharedModule --- frontend/module/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/module/main.ts b/frontend/module/main.ts index 7435e976b80d..30da5019e648 100644 --- a/frontend/module/main.ts +++ b/frontend/module/main.ts @@ -28,7 +28,7 @@ //++ import { Injector, NgModule } from '@angular/core'; -import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service'; @@ -70,7 +70,7 @@ export function initializeGitlabIntegrationPlugin(injector:Injector) { @NgModule({ imports: [ - OPSharedModule, + OpSharedModule, OpenprojectTabsModule, ], providers: [ From 8f604d896da1be58399cc37386ea6147fa731bc8 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Fri, 24 Mar 2023 09:54:53 -0400 Subject: [PATCH 33/69] Readme update --- README.md | 10 ++++++---- openproject-gitlab_integration.gemspec | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 28c0328aadd6..bee2566474c1 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.8 GA +## NEW VERSION 2.0.9 GA -Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). +Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github. This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). preview -This 2.x version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. ## Introduction OpenProject module for integration with Gitlab: * Latest Gitlab release tested: **15.9** -* Latest OpenProject release tested: **12.4.5** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) +* Latest OpenProject release tested: **12.5.1** + * for OpenProject versions earlier than 12.4.5 use v2.0.8 + * for OpenProject versions earlier than 12.2.0 use v2.0.5 + * for OpenProject versions earlier than 12.1.0 use v2.0.4 This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 3f2fdc68cc0a..41ddb2632a40 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.8' + s.version = '2.0.9' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From c4b8b3ab0744989fb6bf430cce848ced73a49e06 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Fri, 30 Jun 2023 12:29:06 +0300 Subject: [PATCH 34/69] fix version in Gemfile.lock 2.0.6 -> 2.0.9 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bee2566474c1..68e367311985 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.6) + openproject-gitlab_integration (2.0.9) openproject-webhooks ``` From 88613af014ae58195191cfd90d420242bda717da Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Mon, 20 Nov 2023 11:25:42 -0500 Subject: [PATCH 35/69] New version 2.1 --- README.md | 20 +- app/models/gitlab_issue.rb | 86 ++++++ app/models/gitlab_merge_request.rb | 1 - app/models/gitlab_pipeline.rb | 18 +- config/locales/crowdin/js-ar.yml | 11 +- config/locales/crowdin/js-bg.yml | 11 +- config/locales/crowdin/js-ca.yml | 11 +- config/locales/crowdin/js-cs.yml | 11 +- config/locales/crowdin/js-da.yml | 11 +- config/locales/crowdin/js-de.yml | 11 +- config/locales/crowdin/js-el.yml | 11 +- config/locales/crowdin/js-es.yml | 11 +- config/locales/crowdin/js-fi.yml | 11 +- config/locales/crowdin/js-fil.yml | 11 +- config/locales/crowdin/js-fr.yml | 11 +- config/locales/crowdin/js-hr.yml | 11 +- config/locales/crowdin/js-hu.yml | 11 +- config/locales/crowdin/js-id.yml | 11 +- config/locales/crowdin/js-it.yml | 11 +- config/locales/crowdin/js-ja.yml | 11 +- config/locales/crowdin/js-ko.yml | 11 +- config/locales/crowdin/js-lt.yml | 11 +- config/locales/crowdin/js-nl.yml | 11 +- config/locales/crowdin/js-no.yml | 11 +- config/locales/crowdin/js-pl.yml | 11 +- config/locales/crowdin/js-pt.yml | 11 +- config/locales/crowdin/js-ro.yml | 11 +- config/locales/crowdin/js-ru.yml | 11 +- config/locales/crowdin/js-sk.yml | 11 +- config/locales/crowdin/js-sl.yml | 11 +- config/locales/crowdin/js-sv.yml | 11 +- config/locales/crowdin/js-tr.yml | 11 +- config/locales/crowdin/js-uk.yml | 11 +- config/locales/crowdin/js-vi.yml | 11 +- config/locales/crowdin/js-zh-CN.yml | 11 +- config/locales/crowdin/js-zh-TW.yml | 11 +- config/locales/js-en.yml | 7 +- ...110001_add_username_commit_to_pipelines.rb | 36 +++ .../20211015110002_add_gitlab_issues.rb | 56 ++++ .../git-actions-menu.component.ts | 16 +- .../git-actions-menu.directive.ts | 2 +- .../styles/git-actions-menu.sass | 2 +- .../module/git-actions/git-actions.service.ts | 2 +- .../gitlab-tab/gitlab-tab.component.sass | 36 +++ .../module/gitlab-tab/gitlab-tab.component.ts | 7 +- .../gitlab-tab/gitlab-tab.template.html | 136 ++++++++- ...s-resource.ts => gitlab-issue-resource.ts} | 4 +- .../gitlab-merge-request-resource.ts | 2 +- .../hal/resources/gitlab-user-resource.ts | 2 +- frontend/module/icons/gitlab-icons.svg | 99 +++++++ frontend/module/issue/issue.component.html | 51 ++++ frontend/module/issue/issue.component.sass | 151 ++++++++++ frontend/module/issue/issue.component.ts | 72 +++++ frontend/module/main.ts | 42 ++- .../merge-request.component.html | 70 ++--- .../merge-request.component.sass | 263 +++++++++++++++--- .../merge-request/merge-request.component.ts | 54 +--- .../merge-request/mr-pipeline.component.sass | 16 +- .../styles/tab-header-issue.sass} | 12 +- .../tab-header-issue.component.ts | 50 ++++ .../tab-header-issue.template.html | 6 + .../tab-header-mr/styles/tab-header-mr.sass | 51 ++++ .../tab-header-mr.component.ts} | 16 +- .../tab-header-mr.template.html} | 2 +- .../module/tab-issue/tab-issue.component.ts | 70 +++++ .../module/tab-issue/tab-issue.template.html | 5 + .../tab-issue/wp-gitlab-issue.service.ts | 52 ++++ frontend/module/tab-mrs/tab-mrs.component.ts | 2 +- .../module/tab-mrs/wp-gitlab-mrs.service.ts | 2 +- frontend/module/typings.d.ts | 24 +- .../gitlab_issue_collection_representer.rb | 40 +++ .../gitlab_issues/gitlab_issue_representer.rb | 90 ++++++ .../gitlab_issues_by_work_package_api.rb | 53 ++++ .../gitlab_issues/gitlab_user_representer.rb | 52 ++++ .../gitlab_pipeline_representer.rb | 2 + lib/open_project/gitlab_integration/engine.rb | 8 + .../notification_handler/issue_hook.rb | 14 + .../merge_request_hook.rb | 10 +- .../notification_handler/pipeline_hook.rb | 8 +- .../patches/api/work_package_representer.rb | 9 + .../patches/work_package_patch.rb | 1 + .../services/upsert_issue.rb | 84 ++++++ .../services/upsert_pipeline.rb | 14 +- openproject-gitlab_integration.gemspec | 4 +- 84 files changed, 1977 insertions(+), 307 deletions(-) create mode 100644 app/models/gitlab_issue.rb create mode 100644 db/migrate/20211015110001_add_username_commit_to_pipelines.rb create mode 100644 db/migrate/20211015110002_add_gitlab_issues.rb create mode 100644 frontend/module/gitlab-tab/gitlab-tab.component.sass rename frontend/module/hal/resources/{gitlab-pipelines-resource.ts => gitlab-issue-resource.ts} (94%) create mode 100644 frontend/module/icons/gitlab-icons.svg create mode 100644 frontend/module/issue/issue.component.html create mode 100644 frontend/module/issue/issue.component.sass create mode 100644 frontend/module/issue/issue.component.ts rename frontend/module/{tab-header/styles/tab-header.sass => tab-header-issue/styles/tab-header-issue.sass} (90%) create mode 100644 frontend/module/tab-header-issue/tab-header-issue.component.ts create mode 100644 frontend/module/tab-header-issue/tab-header-issue.template.html create mode 100644 frontend/module/tab-header-mr/styles/tab-header-mr.sass rename frontend/module/{tab-header/tab-header.component.ts => tab-header-mr/tab-header-mr.component.ts} (83%) rename frontend/module/{tab-header/tab-header.template.html => tab-header-mr/tab-header-mr.template.html} (91%) create mode 100644 frontend/module/tab-issue/tab-issue.component.ts create mode 100644 frontend/module/tab-issue/tab-issue.template.html create mode 100644 frontend/module/tab-issue/wp-gitlab-issue.service.ts create mode 100644 lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb create mode 100644 lib/api/v3/gitlab_issues/gitlab_issue_representer.rb create mode 100644 lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb create mode 100644 lib/api/v3/gitlab_issues/gitlab_user_representer.rb create mode 100644 lib/open_project/gitlab_integration/services/upsert_issue.rb diff --git a/README.md b/README.md index 68e367311985..21d9fe0b6d44 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,17 @@ # openproject-gitlab-integration -## NEW VERSION 2.0.9 GA +## NEW VERSION 2.0.6 GA -Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github. This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). +Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). -preview +![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) +This 2.x version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. ## Introduction OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **15.9** -* Latest OpenProject release tested: **12.5.1** - * for OpenProject versions earlier than 12.4.5 use v2.0.8 - * for OpenProject versions earlier than 12.2.0 use v2.0.5 - * for OpenProject versions earlier than 12.1.0 use v2.0.4 +* Latest Gitlab release tested: **15.3.1** +* Latest OpenProject release tested: **12.2.1** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). @@ -159,7 +157,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.9) + openproject-gitlab_integration (2.0.6) openproject-webhooks ``` @@ -210,10 +208,10 @@ In Gitlab you have to set up a webhook in each repository to be integrated with You need to configure just two things in the webhook: -1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key with previouslly generated token as value. In the end the URL should look something like this: +1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: ``` - http://openproject-url.com/webhooks/gitlab?key=generated_access_token + http://openproject-url.com/webhooks/gitlab?key=ae278268 ``` 2. Enable the required triggers: diff --git a/app/models/gitlab_issue.rb b/app/models/gitlab_issue.rb new file mode 100644 index 000000000000..d7fe501a8a4c --- /dev/null +++ b/app/models/gitlab_issue.rb @@ -0,0 +1,86 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +class GitlabIssue < ApplicationRecord + LABEL_KEYS = %w[color title].freeze + + has_and_belongs_to_many :work_packages + belongs_to :gitlab_user, optional: true + + enum state: { + opened: 'opened', + closed: 'closed' + } + + validates_presence_of :gitlab_html_url, + :number, + :repository, + :state, + :title, + :gitlab_updated_at + validates_presence_of :body, + unless: :partial? + validate :validate_labels_schema + + scope :without_work_package, -> { left_outer_joins(:work_packages).where(work_packages: { id: nil }) } + + def self.find_by_gitlab_identifiers(id: nil, url: nil, initialize: false) + raise ArgumentError, "needs an id or an url" if id.nil? && url.blank? + + found = where(gitlab_id: id).or(where(gitlab_html_url: url)).take + + if found + found + elsif initialize + new(gitlab_id: id, gitlab_html_url: url) + end + end + + def partial? + [body].all?(&:nil?) + end + + private + + def validate_labels_schema + return if labels.nil? + return if labels.all? { |label| label.keys.sort == LABEL_KEYS } + + errors.add(:labels, 'invalid schema') + end + + def with_logging + yield if block_given? + rescue StandardError => e + Rails.logger.error "Error at gitlab issue: #{e} #{e.message}" + raise e + end +end diff --git a/app/models/gitlab_merge_request.rb b/app/models/gitlab_merge_request.rb index ca99e18eb59a..4449e85810b2 100644 --- a/app/models/gitlab_merge_request.rb +++ b/app/models/gitlab_merge_request.rb @@ -34,7 +34,6 @@ class GitlabMergeRequest < ApplicationRecord has_and_belongs_to_many :work_packages has_many :gitlab_pipelines, dependent: :destroy - has_many :github_check_runs, dependent: :destroy belongs_to :gitlab_user, optional: true belongs_to :merged_by, optional: true, class_name: 'GitlabUser' diff --git a/app/models/gitlab_pipeline.rb b/app/models/gitlab_pipeline.rb index 4433ad3e8409..24b4c1633122 100644 --- a/app/models/gitlab_pipeline.rb +++ b/app/models/gitlab_pipeline.rb @@ -34,15 +34,25 @@ class GitlabPipeline < ApplicationRecord # TODO: confirm with the gitlab documentation what are the different statuses. enum status: { - completed: 'completed', - in_progress: 'in_progress', + created: 'created', + running: 'running', success: 'success', - queued: 'queued' + waiting: 'waiting', + preparing: 'preparing', + failed: 'failed', + pending: 'pending', + canceled: 'canceled', + skipped: 'skipped', + manual: 'manual', + scheduled: 'scheduled' } validates_presence_of :gitlab_user_avatar_url, :gitlab_html_url, :gitlab_id, :status, - :name + :name, + :ci_details, + :commit_id, + :username end diff --git a/config/locales/crowdin/js-ar.yml b/config/locales/crowdin/js-ar.yml index 3e2028a64cbf..f575fa9efb78 100644 --- a/config/locales/crowdin/js-ar.yml +++ b/config/locales/crowdin/js-ar.yml @@ -31,7 +31,9 @@ ar: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ ar: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-bg.yml b/config/locales/crowdin/js-bg.yml index 3cd8fa468f3e..78c2bb6ec51a 100644 --- a/config/locales/crowdin/js-bg.yml +++ b/config/locales/crowdin/js-bg.yml @@ -31,7 +31,9 @@ bg: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ bg: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-ca.yml b/config/locales/crowdin/js-ca.yml index c0204c42e067..723b26e95bc5 100644 --- a/config/locales/crowdin/js-ca.yml +++ b/config/locales/crowdin/js-ca.yml @@ -31,7 +31,9 @@ ca: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ ca: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-cs.yml b/config/locales/crowdin/js-cs.yml index a5d495518c2c..a8905380a2be 100644 --- a/config/locales/crowdin/js-cs.yml +++ b/config/locales/crowdin/js-cs.yml @@ -31,7 +31,9 @@ cs: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ cs: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-da.yml b/config/locales/crowdin/js-da.yml index eac4cb5ec5cd..c168aaeb6d57 100644 --- a/config/locales/crowdin/js-da.yml +++ b/config/locales/crowdin/js-da.yml @@ -31,7 +31,9 @@ da: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ da: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-de.yml b/config/locales/crowdin/js-de.yml index b194a00b07c3..066a38968913 100644 --- a/config/locales/crowdin/js-de.yml +++ b/config/locales/crowdin/js-de.yml @@ -31,7 +31,9 @@ de: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ de: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-el.yml b/config/locales/crowdin/js-el.yml index cb195d5a8e68..6ae410420ed6 100644 --- a/config/locales/crowdin/js-el.yml +++ b/config/locales/crowdin/js-el.yml @@ -31,7 +31,9 @@ el: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ el: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-es.yml b/config/locales/crowdin/js-es.yml index bcd3948614fa..4955957a802d 100644 --- a/config/locales/crowdin/js-es.yml +++ b/config/locales/crowdin/js-es.yml @@ -31,7 +31,9 @@ es: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ es: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-fi.yml b/config/locales/crowdin/js-fi.yml index cc7472c9b9b8..007068090482 100644 --- a/config/locales/crowdin/js-fi.yml +++ b/config/locales/crowdin/js-fi.yml @@ -31,7 +31,9 @@ fi: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ fi: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-fil.yml b/config/locales/crowdin/js-fil.yml index 8ec030ecc1d4..5df298917221 100644 --- a/config/locales/crowdin/js-fil.yml +++ b/config/locales/crowdin/js-fil.yml @@ -31,7 +31,9 @@ fil: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ fil: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-fr.yml b/config/locales/crowdin/js-fr.yml index 1a8b5fa42d92..a4cc350aacd5 100644 --- a/config/locales/crowdin/js-fr.yml +++ b/config/locales/crowdin/js-fr.yml @@ -31,7 +31,9 @@ fr: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ fr: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-hr.yml b/config/locales/crowdin/js-hr.yml index 4a7ba75de548..cb34ab81c26f 100644 --- a/config/locales/crowdin/js-hr.yml +++ b/config/locales/crowdin/js-hr.yml @@ -31,7 +31,9 @@ hr: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ hr: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-hu.yml b/config/locales/crowdin/js-hu.yml index 1dc122fadd7d..b584ba1d94d4 100644 --- a/config/locales/crowdin/js-hu.yml +++ b/config/locales/crowdin/js-hu.yml @@ -31,7 +31,9 @@ hu: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ hu: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-id.yml b/config/locales/crowdin/js-id.yml index 87355960b03f..922c5bf1d2d2 100644 --- a/config/locales/crowdin/js-id.yml +++ b/config/locales/crowdin/js-id.yml @@ -31,7 +31,9 @@ id: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ id: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-it.yml b/config/locales/crowdin/js-it.yml index c8a86bffd993..f1c7409c29bb 100644 --- a/config/locales/crowdin/js-it.yml +++ b/config/locales/crowdin/js-it.yml @@ -31,7 +31,9 @@ it: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ it: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-ja.yml b/config/locales/crowdin/js-ja.yml index 153e3ef8a0d5..40a8e5fb36ce 100644 --- a/config/locales/crowdin/js-ja.yml +++ b/config/locales/crowdin/js-ja.yml @@ -31,7 +31,9 @@ ja: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ ja: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-ko.yml b/config/locales/crowdin/js-ko.yml index 183450ed5cf5..3f58d75b8801 100644 --- a/config/locales/crowdin/js-ko.yml +++ b/config/locales/crowdin/js-ko.yml @@ -31,7 +31,9 @@ ko: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ ko: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-lt.yml b/config/locales/crowdin/js-lt.yml index 8e71278d5799..b612d1be67dc 100644 --- a/config/locales/crowdin/js-lt.yml +++ b/config/locales/crowdin/js-lt.yml @@ -31,7 +31,9 @@ lt: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ lt: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-nl.yml b/config/locales/crowdin/js-nl.yml index ff8213d5df64..b0c8d39b54b9 100644 --- a/config/locales/crowdin/js-nl.yml +++ b/config/locales/crowdin/js-nl.yml @@ -31,7 +31,9 @@ nl: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ nl: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-no.yml b/config/locales/crowdin/js-no.yml index e5e6a941c5ed..d8983e51a0c5 100644 --- a/config/locales/crowdin/js-no.yml +++ b/config/locales/crowdin/js-no.yml @@ -31,7 +31,9 @@ gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-pl.yml b/config/locales/crowdin/js-pl.yml index 43b90e423664..35e9c52f07b7 100644 --- a/config/locales/crowdin/js-pl.yml +++ b/config/locales/crowdin/js-pl.yml @@ -31,7 +31,9 @@ pl: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ pl: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-pt.yml b/config/locales/crowdin/js-pt.yml index e61df0b657b5..b265a3a11bc2 100644 --- a/config/locales/crowdin/js-pt.yml +++ b/config/locales/crowdin/js-pt.yml @@ -31,7 +31,9 @@ pt: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ pt: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-ro.yml b/config/locales/crowdin/js-ro.yml index 9e134ca512de..fc8bbb0003e6 100644 --- a/config/locales/crowdin/js-ro.yml +++ b/config/locales/crowdin/js-ro.yml @@ -31,7 +31,9 @@ ro: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ ro: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index a51af8892daf..a71c435d49db 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -31,7 +31,9 @@ ru: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ ru: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-sk.yml b/config/locales/crowdin/js-sk.yml index 5e52f2533eab..2899069e371c 100644 --- a/config/locales/crowdin/js-sk.yml +++ b/config/locales/crowdin/js-sk.yml @@ -31,7 +31,9 @@ sk: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ sk: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-sl.yml b/config/locales/crowdin/js-sl.yml index fa126147b637..3cbbfd9a6415 100644 --- a/config/locales/crowdin/js-sl.yml +++ b/config/locales/crowdin/js-sl.yml @@ -31,7 +31,9 @@ sl: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ sl: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-sv.yml b/config/locales/crowdin/js-sv.yml index 589944e75275..48f132e89919 100644 --- a/config/locales/crowdin/js-sv.yml +++ b/config/locales/crowdin/js-sv.yml @@ -31,7 +31,9 @@ sv: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ sv: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-tr.yml b/config/locales/crowdin/js-tr.yml index 1d2bff4723b2..c5e59b0d4668 100644 --- a/config/locales/crowdin/js-tr.yml +++ b/config/locales/crowdin/js-tr.yml @@ -31,7 +31,9 @@ tr: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ tr: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-uk.yml b/config/locales/crowdin/js-uk.yml index a594075b4278..b3088532bdd7 100644 --- a/config/locales/crowdin/js-uk.yml +++ b/config/locales/crowdin/js-uk.yml @@ -31,7 +31,9 @@ uk: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ uk: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-vi.yml b/config/locales/crowdin/js-vi.yml index 96ff09725bb2..e1dffccfdeb5 100644 --- a/config/locales/crowdin/js-vi.yml +++ b/config/locales/crowdin/js-vi.yml @@ -31,7 +31,9 @@ vi: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ vi: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-zh-CN.yml b/config/locales/crowdin/js-zh-CN.yml index 356106c814d3..59d1d96f0233 100644 --- a/config/locales/crowdin/js-zh-CN.yml +++ b/config/locales/crowdin/js-zh-CN.yml @@ -31,7 +31,9 @@ zh-CN: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ zh-CN: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/crowdin/js-zh-TW.yml b/config/locales/crowdin/js-zh-TW.yml index f25cb2161f9c..2b5dddb91aa6 100644 --- a/config/locales/crowdin/js-zh-TW.yml +++ b/config/locales/crowdin/js-zh-TW.yml @@ -31,7 +31,9 @@ zh-TW: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -46,6 +48,9 @@ zh-TW: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} in the MR title/description or create a new MR. - gitlab_actions: Actions + empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. + gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index dd2d881a9197..fac3b06a28ff 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -32,7 +32,9 @@ en: gitlab_integration: work_packages: tab_name: "GitLab" - tab_header: + tab_header_issue: + title: "Issues" + tab_header_mr: title: "Merge requests" create_mr: label: Create MR @@ -47,6 +49,9 @@ en: title: Quick snippets for Git copy_success: ✅ Copied! copy_error: ❌ Copy failed! + tab_issue: + empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. tab_mrs: empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. gitlab_pipelines: Pipelines + updated_on: Updated on diff --git a/db/migrate/20211015110001_add_username_commit_to_pipelines.rb b/db/migrate/20211015110001_add_username_commit_to_pipelines.rb new file mode 100644 index 000000000000..2b621fa742d5 --- /dev/null +++ b/db/migrate/20211015110001_add_username_commit_to_pipelines.rb @@ -0,0 +1,36 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +class AddUsernameCommitToPipelines < ActiveRecord::Migration[7.0] + def change + add_column :gitlab_pipelines, :username, :text + + add_column :gitlab_pipelines, :commit_id, :text + end +end diff --git a/db/migrate/20211015110002_add_gitlab_issues.rb b/db/migrate/20211015110002_add_gitlab_issues.rb new file mode 100644 index 000000000000..2e046b29d5c1 --- /dev/null +++ b/db/migrate/20211015110002_add_gitlab_issues.rb @@ -0,0 +1,56 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +class AddGitlabIssues < ActiveRecord::Migration[7.0] + # rubocop:disable Metrics/AbcSize + def change + create_table :gitlab_issues do |t| + t.references :gitlab_user + + t.bigint :gitlab_id, unique: true + t.integer :number, null: false + t.string :gitlab_html_url, null: false, unique: true + t.string :state, null: false + t.string :repository, null: false + t.datetime :gitlab_updated_at + t.string :title + t.text :body + t.json :labels # [{name, color}] + t.timestamps + end + + create_join_table :gitlab_issues, :work_packages do |t| + t.index :gitlab_issue_id, name: 'gitlab_issues_wp_issue_id' + t.index %i[gitlab_issue_id work_package_id], + unique: true, + name: "unique_index_gl_issues_wps_on_gl_issue_id_and_wp_id" + end + + end + # rubocop:enable Metrics/AbcSize +end diff --git a/frontend/module/git-actions-menu/git-actions-menu.component.ts b/frontend/module/git-actions-menu/git-actions-menu.component.ts index 80ecfe7b0f7b..a4130a908783 100644 --- a/frontend/module/git-actions-menu/git-actions-menu.component.ts +++ b/frontend/module/git-actions-menu/git-actions-menu.component.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -51,11 +51,11 @@ export class GitActionsMenuComponent extends OPContextMenuComponent { @Input() public workPackage:WorkPackageResource; public text = { - title: this.I18n.t('js.gitlab_integration.tab_header.git_actions.title'), - copyButtonHelpText: this.I18n.t('js.gitlab_integration.tab_header.git_actions.copy_button_help'), + title: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.title'), + copyButtonHelpText: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.copy_button_help'), copyResult: { - success: this.I18n.t('js.gitlab_integration.tab_header.git_actions.copy_success'), - error: this.I18n.t('js.gitlab_integration.tab_header.git_actions.copy_error') + success: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.copy_success'), + error: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.copy_error') } }; @@ -66,17 +66,17 @@ export class GitActionsMenuComponent extends OPContextMenuComponent { public snippets:ISnippet[] = [ { id: 'branch', - name: this.I18n.t('js.gitlab_integration.tab_header.git_actions.branch_name'), + name: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.branch_name'), textToCopy: () => this.gitActions.branchName(this.workPackage) }, { id: 'message', - name: this.I18n.t('js.gitlab_integration.tab_header.git_actions.commit_message'), + name: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.commit_message'), textToCopy: () => this.gitActions.commitMessage(this.workPackage) }, { id: 'command', - name: this.I18n.t('js.gitlab_integration.tab_header.git_actions.cmd'), + name: this.I18n.t('js.gitlab_integration.tab_header_mr.git_actions.cmd'), textToCopy: () => this.gitActions.gitCommand(this.workPackage) }, ]; diff --git a/frontend/module/git-actions-menu/git-actions-menu.directive.ts b/frontend/module/git-actions-menu/git-actions-menu.directive.ts index 62e54b0d74fd..d20b2fc18618 100644 --- a/frontend/module/git-actions-menu/git-actions-menu.directive.ts +++ b/frontend/module/git-actions-menu/git-actions-menu.directive.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/git-actions-menu/styles/git-actions-menu.sass b/frontend/module/git-actions-menu/styles/git-actions-menu.sass index 4ad868111408..b502c7bcd3a3 100644 --- a/frontend/module/git-actions-menu/styles/git-actions-menu.sass +++ b/frontend/module/git-actions-menu/styles/git-actions-menu.sass @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/git-actions/git-actions.service.ts b/frontend/module/git-actions/git-actions.service.ts index 7a86e1a05ba3..1c87adb9c1fc 100644 --- a/frontend/module/git-actions/git-actions.service.ts +++ b/frontend/module/git-actions/git-actions.service.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/gitlab-tab/gitlab-tab.component.sass b/frontend/module/gitlab-tab/gitlab-tab.component.sass new file mode 100644 index 000000000000..06056041ad2d --- /dev/null +++ b/frontend/module/gitlab-tab/gitlab-tab.component.sass @@ -0,0 +1,36 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +@import "helpers" + +:host, +.gitlab-header + position: sticky + top: 0 + z-index: 850 // Ensure the header is above other content diff --git a/frontend/module/gitlab-tab/gitlab-tab.component.ts b/frontend/module/gitlab-tab/gitlab-tab.component.ts index bcd36c1a0932..c7f28a1a2536 100644 --- a/frontend/module/gitlab-tab/gitlab-tab.component.ts +++ b/frontend/module/gitlab-tab/gitlab-tab.component.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -35,7 +35,10 @@ import { PathHelperService } from "core-app/core/path-helper/path-helper.service @Component({ selector: 'gitlab-tab', - templateUrl: './gitlab-tab.template.html' + templateUrl: './gitlab-tab.template.html', + styleUrls: [ + './gitlab-tab.component.sass', + ] }) export class GitlabTabComponent implements TabComponent { @Input() public workPackage:WorkPackageResource; diff --git a/frontend/module/gitlab-tab/gitlab-tab.template.html b/frontend/module/gitlab-tab/gitlab-tab.template.html index 7bbfd3bd4bc5..a7c04a6dfc13 100644 --- a/frontend/module/gitlab-tab/gitlab-tab.template.html +++ b/frontend/module/gitlab-tab/gitlab-tab.template.html @@ -1,2 +1,136 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/module/hal/resources/gitlab-pipelines-resource.ts b/frontend/module/hal/resources/gitlab-issue-resource.ts similarity index 94% rename from frontend/module/hal/resources/gitlab-pipelines-resource.ts rename to frontend/module/hal/resources/gitlab-issue-resource.ts index 7e01eb4ccb67..f095a717e933 100644 --- a/frontend/module/hal/resources/gitlab-pipelines-resource.ts +++ b/frontend/module/hal/resources/gitlab-issue-resource.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -29,7 +29,7 @@ import { HalResource } from "core-app/features/hal/resources/hal-resource"; -export class GitlabPipelineResource extends HalResource { +export class GitlabIssueResource extends HalResource { public get state() { return this.states.projects.get(this.id!) as any; } diff --git a/frontend/module/hal/resources/gitlab-merge-request-resource.ts b/frontend/module/hal/resources/gitlab-merge-request-resource.ts index 23163b38f0b0..49a6c510a672 100644 --- a/frontend/module/hal/resources/gitlab-merge-request-resource.ts +++ b/frontend/module/hal/resources/gitlab-merge-request-resource.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/hal/resources/gitlab-user-resource.ts b/frontend/module/hal/resources/gitlab-user-resource.ts index 3a18da80ba5d..c729a5b82d75 100644 --- a/frontend/module/hal/resources/gitlab-user-resource.ts +++ b/frontend/module/hal/resources/gitlab-user-resource.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/icons/gitlab-icons.svg b/frontend/module/icons/gitlab-icons.svg new file mode 100644 index 000000000000..13acb8a8da1d --- /dev/null +++ b/frontend/module/icons/gitlab-icons.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/module/issue/issue.component.html b/frontend/module/issue/issue.component.html new file mode 100644 index 000000000000..284db7059d56 --- /dev/null +++ b/frontend/module/issue/issue.component.html @@ -0,0 +1,51 @@ +
+ + + + {{state}} | + + + + + +
+ {{ text.label_created_by }} + + + . + + + {{ text.label_last_updated_on }} + + . +
+ + diff --git a/frontend/module/issue/issue.component.sass b/frontend/module/issue/issue.component.sass new file mode 100644 index 000000000000..9d3e946adf66 --- /dev/null +++ b/frontend/module/issue/issue.component.sass @@ -0,0 +1,151 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +@import "helpers" + +:host, +.op-issue + display: grid + grid-template-columns: minmax(270px, auto) auto 1fr + grid-template-areas: "title title title" "info_created info_created info_created" "state label_icon label_icon" "label label label" + margin-bottom: 5px + margin-top: 5px + padding-bottom: 4px + padding-top: 4px + border-bottom: 1px solid #dddddd + + &:last-child + border-bottom: none + + &--iconsize + width: 16px + height: 16px + vertical-align: -3px + + &--title + @include text-shortener + font-weight: bold + grid-area: title + line-height: 22px + margin-right: 20px + + &--avatar + grid-area: info + + &--info_created + @include text-shortener + display: block + grid-area: info_created + font-size: 0.75rem + color: var(--gray-dark) + margin-bottom: 3px + + &--info_updated + @include text-shortener + display: block + grid-area: info_updated + font-size: 0.75rem + color: var(--gray-dark) + margin-top: -4px + + &--username + color: var(--gray-dark) + text-decoration: none + + &--labels + display: none + grid-area: label + font-size: 0.9rem + color: var(--gray-dark) + margin-top: 10px + + &--label + display: inline-block + padding: 2px 0.5rem 2px 0.5rem + border-radius: 10rem + border: 1px solid #fff + color: #fff + font-size: 0.8rem + + &--link + font-size: 0.8rem + text-decoration: none + + &_open + background-color: #c3e6cd + color: #24663b + fill: #24663b + + &_closed + background-color: #cbe2f9 + color: #0b5cad + fill: #0b5cad + + &--label_icon + grid-area: label_icon + display: inline-block + padding: 4px 6px + border-radius: 10rem + font-size: 0.8rem + font-weight: 400 + align-self: center + width: fit-content + margin-right: 6px + border: 1px solid #cecdd4 + background-color: #f3f3f3 + + &:hover + border: 1px solid #b6b5ba + background-color: #e4e4e4 + + &_style + fill: #737278 + + &--state + grid-area: state + display: inline-block + padding: 5px 0.6rem 5px 0.6rem + border-radius: 10rem + font-size: 0.8rem + font-weight: 400 + text-transform: capitalize + align-self: center + white-space: nowrap + width: fit-content + margin-right: 6px + + &_open + background-color: #c3e6cd + color: #24663b + fill: #24663b + + &_closed + background-color: #cbe2f9 + color: #0b5cad + fill: #0b5cad diff --git a/frontend/module/issue/issue.component.ts b/frontend/module/issue/issue.component.ts new file mode 100644 index 000000000000..3c260b8186d1 --- /dev/null +++ b/frontend/module/issue/issue.component.ts @@ -0,0 +1,72 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { Component, Input } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import {IGitlabIssueResource} from 'core-app/features/plugins/linked/openproject-gitlab_integration/typings'; + +@Component({ + selector: 'gitlab-issue', + templateUrl: './issue.component.html', + styleUrls: [ + './issue.component.sass', + ], + host: { class: 'op-issue' } +}) + +export class IssueComponent { + @Input() public gitlabIssue:IGitlabIssueResource; + + public text = { + label_created_by: this.I18n.t('js.label_created_by'), + label_last_updated_on: this.I18n.t('js.gitlab_integration.updated_on'), + label_details: this.I18n.t('js.label_details'), + }; + + constructor(readonly PathHelper:PathHelperService, + readonly I18n:I18nService) { + } + + get state() { + + if (this.gitlabIssue.state === 'opened') { + return ('open'); + } else { + return('closed'); + } + } + + toggleLabels(identifier: string) { + const labelsElement = document.querySelector(`.op-issue--labels-${identifier}`) as HTMLElement; + + // Check the current display property and toggle it + labelsElement.style.display = labelsElement.style.display === 'none' ? 'block' : 'none'; + } +} diff --git a/frontend/module/main.ts b/frontend/module/main.ts index 30da5019e648..a694e4d3b1ea 100644 --- a/frontend/module/main.ts +++ b/frontend/module/main.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -34,26 +34,38 @@ import { WorkPackageTabsService } from 'core-app/features/work-packages/componen import { GitlabTabComponent } from './gitlab-tab/gitlab-tab.component'; -import { TabHeaderComponent } from './tab-header/tab-header.component'; +import { TabHeaderMrsComponent } from './tab-header-mr/tab-header-mr.component'; +import { TabHeaderIssueComponent } from './tab-header-issue/tab-header-issue.component'; import { TabMrsComponent } from './tab-mrs/tab-mrs.component'; +import { TabIssueComponent } from './tab-issue/tab-issue.component'; import { GitActionsMenuDirective } from './git-actions-menu/git-actions-menu.directive'; import { GitActionsMenuComponent } from './git-actions-menu/git-actions-menu.component'; import { WorkPackagesGitlabMrsService } from './tab-mrs/wp-gitlab-mrs.service'; +import { WorkPackagesGitlabIssueService } from './tab-issue/wp-gitlab-issue.service'; import { MergeRequestComponent } from './merge-request/merge-request.component'; +import { IssueComponent } from './issue/issue.component'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { Observable } from 'rxjs'; +import { Observable, combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -export function workPackageGitlabMrsCount( +export function workPackageGitlabCount( workPackage:WorkPackageResource, injector:Injector, ):Observable { const gitlabMrsService = injector.get(WorkPackagesGitlabMrsService); - return gitlabMrsService - .requireAndStream(workPackage) - .pipe( - map((mrs) => mrs.length), - ); + const gitlabIssueService = injector.get(WorkPackagesGitlabIssueService); + + const mrsObservable = gitlabMrsService.requireAndStream(workPackage).pipe( + map((mrs) => mrs.length), + ); + + const issuesObservable = gitlabIssueService.requireAndStream(workPackage).pipe( + map((issues) => issues.length), + ); + + return combineLatest([mrsObservable, issuesObservable]).pipe( + map(([mrsCount, issuesCount]) => mrsCount + issuesCount), + ); } export function initializeGitlabIntegrationPlugin(injector:Injector) { @@ -63,7 +75,7 @@ export function initializeGitlabIntegrationPlugin(injector:Injector) { name: I18n.t('js.gitlab_integration.work_packages.tab_name'), id: 'gitlab', displayable: (workPackage) => !!workPackage.gitlab, - count: workPackageGitlabMrsCount, + count: workPackageGitlabCount, }); } @@ -75,19 +87,25 @@ export function initializeGitlabIntegrationPlugin(injector:Injector) { ], providers: [ WorkPackagesGitlabMrsService, + WorkPackagesGitlabIssueService, ], declarations: [ GitlabTabComponent, - TabHeaderComponent, + TabHeaderMrsComponent, + TabHeaderIssueComponent, TabMrsComponent, + TabIssueComponent, GitActionsMenuDirective, GitActionsMenuComponent, MergeRequestComponent, + IssueComponent, ], exports: [ GitlabTabComponent, - TabHeaderComponent, + TabHeaderMrsComponent, + TabHeaderIssueComponent, TabMrsComponent, + TabIssueComponent, GitActionsMenuDirective, GitActionsMenuComponent, ], diff --git a/frontend/module/merge-request/merge-request.component.html b/frontend/module/merge-request/merge-request.component.html index 94a4d9ad7693..b3507161a0b3 100644 --- a/frontend/module/merge-request/merge-request.component.html +++ b/frontend/module/merge-request/merge-request.component.html @@ -1,69 +1,57 @@ - -
-
+ + + {{state}} | + + + + + + + + {{ mergeRequest.pipelines[0].status }} + + +
{{ text.label_created_by }} - MR author avatar - + />  --> + . - {{ text.label_last_updated_on }} .
- - - {{state}} - - -
+ - - diff --git a/frontend/module/merge-request/merge-request.component.sass b/frontend/module/merge-request/merge-request.component.sass index c7d2a6422590..ef13e6c9f5c9 100644 --- a/frontend/module/merge-request/merge-request.component.sass +++ b/frontend/module/merge-request/merge-request.component.sass @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -32,96 +32,291 @@ :host, .op-merge-request display: grid - grid-template-columns: auto 1fr auto auto - grid-template-areas: "link link link link" "title title title state" "info info info info" "pipeline-label pipeline-label pipeline-label pipeline-label" "pipeline pipeline pipeline pipeline" - margin-bottom: 11px - padding-bottom: 11px + grid-template-columns: minmax(270px, auto) auto auto 1fr + grid-template-areas: "title title title title" "info_created info_created info_created info_created" "state label_icon pipe_icon pipe_icon" "label label label label" + margin-bottom: 5px + margin-top: 5px + padding-bottom: 4px + padding-top: 4px border-bottom: 1px solid #dddddd &:last-child border-bottom: none + &--iconsize + width: 16px + height: 16px + vertical-align: -3px + + &--iconwidth + min-width: 100px + &--title @include text-shortener font-weight: bold grid-area: title - // have the same line height as the status "button" next to it - line-height: 32px + line-height: 22px margin-right: 20px &--avatar grid-area: info - &--info + &--info_created + @include text-shortener display: block - grid-area: info + grid-area: info_created + font-size: 0.75rem + color: var(--gray-dark) margin-bottom: 3px - font-size: 0.9rem - grid-area: info + + &--info_updated + @include text-shortener + display: block + grid-area: info_updated + font-size: 0.75rem color: var(--gray-dark) - // The mini avatar is much higher than the text line. Compensate it. margin-top: -4px + &--username + color: var(--gray-dark) + text-decoration: none + &--labels - display: block - grid-area: info - margin-bottom: 3px + display: none + grid-area: label font-size: 0.9rem - grid-area: info color: var(--gray-dark) - margin-top: 20px + margin-top: 10px &--label display: inline-block - padding: 6px 9px - border-radius: 16px + padding: 2px 0.5rem 2px 0.5rem + border-radius: 10rem border: 1px solid #fff color: #fff font-size: 0.8rem - text-transform: capitalize - - &--date - grid-area: info &--link - grid-area: link - margin-top: 6px - font-size: 0.9rem + font-size: 0.8rem + text-decoration: none + + &_open + background-color: #c3e6cd + color: #24663b + fill: #24663b + + &_ready + background-color: #c3e6cd + color: #24663b + fill: #24663b + + &_merged + background-color: #cbe2f9 + color: #0b5cad + fill: #0b5cad + + &_closed + background-color: #fdd4cd + color: #ae1800 + fill: #ae1800 + + &--label_icon + grid-area: label_icon + display: inline-block + padding: 4px 6px + border-radius: 10rem + font-size: 0.8rem + font-weight: 400 + align-self: center + width: fit-content + margin-right: 6px + border: 1px solid #cecdd4 + background-color: #f3f3f3 + + &:hover + border: 1px solid #b6b5ba + background-color: #e4e4e4 + + &_style + fill: #737278 &--state grid-area: state display: inline-block - padding: 6px 10px + padding: 5px 0.6rem 5px 0.6rem border-radius: 10rem - border: 3px solid #fff - color: #fff font-size: 0.8rem - font-weight: 600 + font-weight: 400 text-transform: capitalize + align-self: center + white-space: nowrap + width: fit-content + margin-right: 6px &_open background-color: #c3e6cd color: #24663b + fill: #24663b &_ready background-color: #c3e6cd color: #24663b + fill: #24663b &_merged background-color: #cbe2f9 color: #0b5cad + fill: #0b5cad &_closed background-color: #fdd4cd color: #ae1800 + fill: #ae1800 + + &--pipeline-icon + grid-area: pipe_icon + padding: 0.25rem + border-radius: 10rem + font-size: 0.8rem + font-weight: 400 + align-self: center + margin-right: 6px + width: fit-content + height: 28px + align-items: center + display: inline-flex + + &_failed + fill: #dd2b0e + width: 20px + height: 20px + + &_bg_failed + background-color: #fdd4cd + + &_success + fill: #108548 + width: 20px + height: 20px + + &_bg_success + background-color: #c3e6cd + + &_running + fill: #367ad1 + width: 20px + height: 20px + + &_bg_running + background-color: #cddffd + + &_created + fill: #737278 + width: 20px + height: 20px + + &_bg_created + background-color: #ececef + + &_waiting + fill: #737278 + width: 20px + height: 20px + + &_bg_waiting + background-color: #ececef + + &_preparing + fill: #737278 + width: 20px + height: 20px + + &_bg_preparing + background-color: #ececef + + &_pending + fill: #737278 + width: 20px + height: 20px + + &_bg_pending + background-color: #ececef + + &_canceled + fill: #737278 + width: 20px + height: 20px + + &_bg_canceled + background-color: #ececef + + &_skipped + fill: #737278 + width: 20px + height: 20px + + &_bg_skipped + background-color: #ececef + + &_manual + fill: #737278 + width: 20px + height: 20px + + &_bg_manual + background-color: #ececef + + &_scheduled + fill: #737278 + width: 20px + height: 20px + + &_bg_scheduled + background-color: #ececef &--pipeline-label - grid-area: pipeline-label - margin-top: 12px - font-size: 0.9rem - font-weight: bold + font-size: 0.8rem + font-weight: 400 + margin-left: 4px + margin-right: 5px + text-transform: capitalize + + &_failed + color: #ae1800 + + &_success + color: #24663b + + &_running + color: #4782d0 + + &_created + color: #737278 + + &_waiting + color: #737278 + + &_preparing + color: #737278 + + &_pending + color: #737278 + + &_canceled + color: #737278 + + &_skipped + color: #737278 + + &_manual + color: #737278 + + &_scheduled + color: #737278 &--pipeline margin-top: 12px + margin-bottom: 12px margin-left: 0 grid-area: pipeline + diff --git a/frontend/module/merge-request/merge-request.component.ts b/frontend/module/merge-request/merge-request.component.ts index bbd469e442f8..1deaddff62e8 100644 --- a/frontend/module/merge-request/merge-request.component.ts +++ b/frontend/module/merge-request/merge-request.component.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -28,7 +28,6 @@ //++ import { Component, Input } from '@angular/core'; -import { GitlabPipelineResource } from '../hal/resources/gitlab-pipelines-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import {IGitlabMergeRequestResource} from 'core-app/features/plugins/linked/openproject-gitlab_integration/typings'; @@ -48,7 +47,7 @@ export class MergeRequestComponent { public text = { label_created_by: this.I18n.t('js.label_created_by'), - label_last_updated_on: this.I18n.t('js.label_last_updated_on'), + label_last_updated_on: this.I18n.t('js.gitlab_integration.updated_on'), label_details: this.I18n.t('js.label_details'), label_pipelines: this.I18n.t('js.gitlab_integration.gitlab_pipelines'), }; @@ -66,52 +65,11 @@ export class MergeRequestComponent { } } - public pipelineStateText(pipeline:GitlabPipelineResource) { - /* Github apps can *optionally* add an output object (and a title) which is the most relevant information to display. - If that is not present, we can display the conclusion (which is present only on finished runs). - If that is not present, we can always fall back to the status. */ - return(pipeline.status); - } + toggleLabels(identifier: string) { + const labelsElement = document.querySelector(`.op-merge-request--labels-${identifier}`) as HTMLElement; - public pipelineState(pipeline:GitlabPipelineResource) { - return(pipeline.status); + // Check the current display property and toggle it + labelsElement.style.display = labelsElement.style.display === 'none' ? 'block' : 'none'; } - public pipelineStateIcon(pipeline:GitlabPipelineResource) { - switch (this.pipelineState(pipeline)) { - case 'success': { - return 'checkmark' - } - case 'queued': { - return 'getting-started' - } - case 'in_progress': { - return 'loading1' - } - case 'failure': { - return 'cancel' - } - case 'timed_out': { - return 'reminder' - } - case 'action_required': { - return 'warning' - } - case 'stale': { - return 'not-supported' - } - case 'skipped': { - return 'redo' - } - case 'neutral': { - return 'minus1' - } - case 'cancelled': { - return 'minus1' - } - default: { - return 'not-supported' - } - } - } } diff --git a/frontend/module/merge-request/mr-pipeline.component.sass b/frontend/module/merge-request/mr-pipeline.component.sass index a346c9a5bdfc..fb773b1dcc28 100644 --- a/frontend/module/merge-request/mr-pipeline.component.sass +++ b/frontend/module/merge-request/mr-pipeline.component.sass @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -29,13 +29,13 @@ .op-mr-pipeline display: grid grid-row: span 4 - grid-template-columns: 28px 33px 1fr auto - grid-template-areas: "pipeline-state-icon pipeline-avatar pipeline-info pipeline-details" + grid-template-columns: 28px 1fr auto // auto + grid-template-areas: "pipeline-state-icon pipeline-info pipeline-avatar" // pipeline-details" list-style-type: none - border: 1px solid #dddddd + border: 1px solid #e4e4e4 padding: 0.3rem 1rem - background: rgba(0, 0, 0, 0.05) - font-size: 0.9rem + // background: rgba(0, 0, 0, 0.05) + font-size: 0.84rem &:first-child border-top-right-radius: 5px @@ -67,13 +67,13 @@ &_queued color: cadetblue - &_in_progress + &_running color: orange &_success color: green - &_failure, + &_failed, &_timed_out, &_action_required, &_stale diff --git a/frontend/module/tab-header/styles/tab-header.sass b/frontend/module/tab-header-issue/styles/tab-header-issue.sass similarity index 90% rename from frontend/module/tab-header/styles/tab-header.sass rename to frontend/module/tab-header-issue/styles/tab-header-issue.sass index f8c45ecd06fa..e5eb014b830a 100644 --- a/frontend/module/tab-header/styles/tab-header.sass +++ b/frontend/module/tab-header-issue/styles/tab-header-issue.sass @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -27,20 +27,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -.gitlab-mr-header +.gitlab-issue-header display: flex flex-wrap: wrap-reverse justify-content: flex-end border-bottom: 1px solid #ddd - + background-color: var(--body-background) .title flex: 1 1 auto border-bottom: 0 margin: 0 - padding: 0 + padding: 0 0 0 4px font-weight: bold font-size: 1rem line-height: 32px text-transform: uppercase + + .s16x32 + width: 16px + height: 32px diff --git a/frontend/module/tab-header-issue/tab-header-issue.component.ts b/frontend/module/tab-header-issue/tab-header-issue.component.ts new file mode 100644 index 000000000000..447a44c7ad3e --- /dev/null +++ b/frontend/module/tab-header-issue/tab-header-issue.component.ts @@ -0,0 +1,50 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { Component, Input } from '@angular/core'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { I18nService } from "core-app/core/i18n/i18n.service"; + +@Component({ + selector: 'tab-header-issue', + templateUrl: './tab-header-issue.template.html', + styleUrls: [ + './styles/tab-header-issue.sass' + ] +}) +export class TabHeaderIssueComponent { + @Input() public workPackage:WorkPackageResource; + + public text = { + title: this.I18n.t('js.gitlab_integration.tab_header_issue.title'), + }; + + constructor(readonly I18n:I18nService) { + } +} diff --git a/frontend/module/tab-header-issue/tab-header-issue.template.html b/frontend/module/tab-header-issue/tab-header-issue.template.html new file mode 100644 index 000000000000..1fc5efe1023f --- /dev/null +++ b/frontend/module/tab-header-issue/tab-header-issue.template.html @@ -0,0 +1,6 @@ +
+ +

+ {{text.title}} +

+
diff --git a/frontend/module/tab-header-mr/styles/tab-header-mr.sass b/frontend/module/tab-header-mr/styles/tab-header-mr.sass new file mode 100644 index 000000000000..1613363e759d --- /dev/null +++ b/frontend/module/tab-header-mr/styles/tab-header-mr.sass @@ -0,0 +1,51 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +.gitlab-mr-header + display: flex + flex-wrap: wrap-reverse + justify-content: flex-end + + border-bottom: 1px solid #ddd + background-color: var(--body-background) + + .title + flex: 1 1 auto + border-bottom: 0 + margin: 0 + padding: 0 0 0 4px + font-weight: bold + font-size: 1rem + line-height: 32px + text-transform: uppercase + align-self: baseline + + .s16x32 + width: 16px + height: 32px diff --git a/frontend/module/tab-header/tab-header.component.ts b/frontend/module/tab-header-mr/tab-header-mr.component.ts similarity index 83% rename from frontend/module/tab-header/tab-header.component.ts rename to frontend/module/tab-header-mr/tab-header-mr.component.ts index 3be83881047c..dc208c5a55cf 100644 --- a/frontend/module/tab-header/tab-header.component.ts +++ b/frontend/module/tab-header-mr/tab-header-mr.component.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -32,21 +32,21 @@ import { WorkPackageResource } from "core-app/features/hal/resources/work-packag import { I18nService } from "core-app/core/i18n/i18n.service"; @Component({ - selector: 'tab-header', - templateUrl: './tab-header.template.html', + selector: 'tab-header-mr', + templateUrl: './tab-header-mr.template.html', styleUrls: [ - './styles/tab-header.sass' + './styles/tab-header-mr.sass' ] }) -export class TabHeaderComponent { +export class TabHeaderMrsComponent { @Input() public workPackage:WorkPackageResource; public text = { - title: this.I18n.t('js.gitlab_integration.tab_header.title'), + title: this.I18n.t('js.gitlab_integration.tab_header_mr.title'), // createPrButtonLabel: this.I18n.t('js.gitlab_integration.tab_header.create_mr.label'), // createPrButtonDescription: this.I18n.t('js.gitlab_integration.tab_header.create_mr.description'), - gitMenuLabel: this.I18n.t('js.gitlab_integration.tab_header.copy_menu.label'), - gitMenuDescription: this.I18n.t('js.gitlab_integration.tab_header.copy_menu.description'), + gitMenuLabel: this.I18n.t('js.gitlab_integration.tab_header_mr.copy_menu.label'), + gitMenuDescription: this.I18n.t('js.gitlab_integration.tab_header_mr.copy_menu.description'), }; constructor(readonly I18n:I18nService) { diff --git a/frontend/module/tab-header/tab-header.template.html b/frontend/module/tab-header-mr/tab-header-mr.template.html similarity index 91% rename from frontend/module/tab-header/tab-header.template.html rename to frontend/module/tab-header-mr/tab-header-mr.template.html index d35fa8317365..031b3c52a576 100644 --- a/frontend/module/tab-header/tab-header.template.html +++ b/frontend/module/tab-header-mr/tab-header-mr.template.html @@ -1,6 +1,6 @@
+

- {{text.title}}

    diff --git a/frontend/module/tab-issue/tab-issue.component.ts b/frontend/module/tab-issue/tab-issue.component.ts new file mode 100644 index 000000000000..28c96b44f791 --- /dev/null +++ b/frontend/module/tab-issue/tab-issue.component.ts @@ -0,0 +1,70 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { I18nService } from "core-app/core/i18n/i18n.service"; +import {IGitlabIssueResource} from "core-app/features/plugins/linked/openproject-gitlab_integration/typings"; +import {ApiV3Service} from "core-app/core/apiv3/api-v3.service"; + +@Component({ + selector: 'tab-issue', + templateUrl: './tab-issue.template.html', + host: { class: 'op-issue' } +}) +export class TabIssueComponent implements OnInit { + @Input() public workPackage:WorkPackageResource; + + public gitlabIssues:IGitlabIssueResource[] = []; + + constructor( + readonly I18n:I18nService, + readonly apiV3Service:ApiV3Service, + readonly halResourceService:HalResourceService, + readonly changeDetector:ChangeDetectorRef, + ) {} + + ngOnInit(): void { + const basePath = this.apiV3Service.work_packages.id(this.workPackage.id as string).path; + const gitlabIssuePath = `${basePath}/gitlab_issues`; + + this.halResourceService + .get>(gitlabIssuePath) + .subscribe((value) => { + this.gitlabIssues = value.elements; + this.changeDetector.detectChanges(); + }); + } + + public getEmptyText() { + return this.I18n.t('js.gitlab_integration.tab_issue.empty',{ wp_id: this.workPackage.id }); + } +} diff --git a/frontend/module/tab-issue/tab-issue.template.html b/frontend/module/tab-issue/tab-issue.template.html new file mode 100644 index 000000000000..ce4a51d334e8 --- /dev/null +++ b/frontend/module/tab-issue/tab-issue.template.html @@ -0,0 +1,5 @@ + +

    +
    + + diff --git a/frontend/module/tab-issue/wp-gitlab-issue.service.ts b/frontend/module/tab-issue/wp-gitlab-issue.service.ts new file mode 100644 index 000000000000..7898eba00762 --- /dev/null +++ b/frontend/module/tab-issue/wp-gitlab-issue.service.ts @@ -0,0 +1,52 @@ +//-- copyright +// OpenProject is an open source project management software. +// Copyright (C) 2023 Ben Tey +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License version 3. +// +// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +// Copyright (C) 2006-2013 Jean-Philippe Lang +// Copyright (C) 2010-2013 the ChiliProject Team +// Copyright (C) 2012-2021 the OpenProject GmbH +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// See docs/COPYRIGHT.rdoc for more details. +//++ + +import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Injectable } from '@angular/core'; +import { ConfigurationService } from "core-app/core/config/configuration.service"; +import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; + +@Injectable() +export class WorkPackagesGitlabIssueService extends WorkPackageLinkedResourceCache { + + constructor(public ConfigurationService:ConfigurationService) { + super(); + } + + protected load(workPackage:WorkPackageResource):Promise { + return workPackage.gitlab_issues.$update().then((data:any) => { + return this.sortList(data.elements); + }); + } + + protected sortList(gitlabIssue:HalResource[], attr = 'createdAt'):HalResource[] { + return _.sortBy(_.flatten(gitlabIssue), attr); + } +} diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/frontend/module/tab-mrs/tab-mrs.component.ts index 4700980decc5..dc4254128171 100644 --- a/frontend/module/tab-mrs/tab-mrs.component.ts +++ b/frontend/module/tab-mrs/tab-mrs.component.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts b/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts index 1e17b490f0aa..aed4c23bea00 100644 --- a/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts +++ b/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. diff --git a/frontend/module/typings.d.ts b/frontend/module/typings.d.ts index 19bce8a38d59..f790de805c73 100644 --- a/frontend/module/typings.d.ts +++ b/frontend/module/typings.d.ts @@ -1,6 +1,6 @@ //-- copyright // OpenProject is an open source project management software. -// Copyright (C) 2021 Ben Tey +// Copyright (C) 2023 Ben Tey // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. @@ -36,6 +36,25 @@ export interface ISnippet { textToCopy:()=>string } +export interface IGitlabIssueResource extends HalResourceClass { + body?:{ + format?:string; + raw?:string; + html?:string; + }, + createdAt?:string; + gitlabUpdatedAt?:string; + htmlUrl?:string; + id?:number; + labels?:string[]; + number?:number; + repository?:string; + state?:string; + title?:string; + updatedAt?:string; + gitlabUser?:IGitlabUserResource; +} + export interface IGitlabMergeRequestResource extends HalResourceClass { body?:{ format?:string; @@ -74,4 +93,7 @@ export interface IGitlabPipelineResource { name:string; startedAt:string; status:string; + ci_details:string[]; + username:string; + commitId:string; } \ No newline at end of file diff --git a/lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb b/lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb new file mode 100644 index 000000000000..f3285a4c62d2 --- /dev/null +++ b/lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb @@ -0,0 +1,40 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module GitlabIssues + class GitlabIssueCollectionRepresenter < ::API::Decorators::Collection + self.to_eager_load = ::API::V3::GitlabIssues::GitlabIssueRepresenter.to_eager_load + end + end + end +end diff --git a/lib/api/v3/gitlab_issues/gitlab_issue_representer.rb b/lib/api/v3/gitlab_issues/gitlab_issue_representer.rb new file mode 100644 index 000000000000..f0043d475517 --- /dev/null +++ b/lib/api/v3/gitlab_issues/gitlab_issue_representer.rb @@ -0,0 +1,90 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'roar/decorator' +require 'roar/json/hal' + +module API + module V3 + module GitlabIssues + class GitlabIssueRepresenter < ::API::Decorators::Single + include API::Caching::CachedRepresenter + include API::Decorators::DateProperty + include API::Decorators::FormattableProperty + include API::Decorators::LinkedResource + + def initialize(model, current_user:, **_opts) + # We force `embed_links` so that github_user and github_check_runs + # are embedded and we can avoid having separate endpoints. + super(model, current_user: current_user, embed_links: true) + end + + cached_representer key_parts: %i[gitlab_user] + + property :id + + property :number + + property :gitlab_html_url, as: :htmlUrl + + property :state, + render_nil: true + + property :repository, + render_nil: true + + date_time_property :gitlab_updated_at, + render_nil: true, + setter: ->(*) { nil } + + property :title, + render_nil: true + + formattable_property :body, + render_nil: true + + property :labels + + associated_resource :gitlab_user, + representer: ::API::V3::GitlabIssues::GitlabUserRepresenter, + link_title_attribute: :gitlab_name + + date_time_property :created_at + + date_time_property :updated_at + + def _type + 'GitlabIssue' + end + + self.to_eager_load = %i[gitlab_user] + end + end + end +end diff --git a/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb b/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb new file mode 100644 index 000000000000..b715058e4242 --- /dev/null +++ b/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb @@ -0,0 +1,53 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module API + module V3 + module GitlabIssues + class GitlabIssuesByWorkPackageAPI < ::API::OpenProjectAPI + after_validation do + authorize(:show_gitlab_content, context: @work_package.project) + @gitlab_issues = @work_package.gitlab_issues + end + + resources :gitlab_issues do + get do + path = api_v3_paths.gitlab_issues_by_work_package(@work_package.id) + GitlabIssueCollectionRepresenter.new(@gitlab_issues, + @gitlab_issues.count, + self_link: path, + current_user: current_user) + end + end + end + end + end +end diff --git a/lib/api/v3/gitlab_issues/gitlab_user_representer.rb b/lib/api/v3/gitlab_issues/gitlab_user_representer.rb new file mode 100644 index 000000000000..46627d1b1735 --- /dev/null +++ b/lib/api/v3/gitlab_issues/gitlab_user_representer.rb @@ -0,0 +1,52 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +require 'roar/decorator' +require 'roar/json/hal' + +module API + module V3 + module GitlabIssues + class GitlabUserRepresenter < ::API::Decorators::Single + include API::Caching::CachedRepresenter + + self_link id_attribute: :id, + title_getter: ->(*) { nil } + + property :gitlab_name, as: :login + property :gitlab_email, as: :email + property :gitlab_avatar_url, as: :avatarUrl + + def _type + 'GitlabUser' + end + end + end + end +end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb b/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb index 0236f90a934d..fc3aab2bf244 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb @@ -46,6 +46,8 @@ class GitlabPipelineRepresenter < ::API::Decorators::Single property :status property :details_url property :ci_details + property :username + property :commit_id, as: :commitId date_time_property :started_at date_time_property :completed_at diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 08da552a24b3..4daf619f8fc9 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -76,6 +76,10 @@ class Engine < ::Rails::Engine "#{work_package(id)}/gitlab_merge_requests" end + add_api_path :gitlab_issues_by_work_package do |id| + "#{work_package(id)}/gitlab_issues" + end + add_api_path :gitlab_user do |id| "gitlab_users/#{id}" end @@ -87,6 +91,10 @@ class Engine < ::Rails::Engine add_api_endpoint 'API::V3::WorkPackages::WorkPackagesAPI', :id do mount ::API::V3::GitlabMergeRequests::GitlabMergeRequestsByWorkPackageAPI end + + add_api_endpoint 'API::V3::WorkPackages::WorkPackagesAPI', :id do + mount ::API::V3::GitlabIssues::GitlabIssuesByWorkPackageAPI + end config.to_prepare do # Register the cron job to clean up old gitlab merge requests diff --git a/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb b/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb index 8f41dfdc2585..0c5cabcc3603 100644 --- a/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb @@ -41,6 +41,7 @@ def process(payload_params) work_packages = find_mentioned_work_packages(text, user) notes = generate_notes(payload) comment_on_referenced_work_packages(work_packages, user, notes) + upsert_issue(work_packages) end private @@ -66,6 +67,19 @@ def generate_notes(payload) :gitlab_user => payload.user.name, :gitlab_user_url => payload.user.avatar_url) end + + def gitlab_issue + @gitlab_issue ||= GitlabIssue + .where(gitlab_id: payload.object_attributes.iid) + .or(GitlabIssue.where(gitlab_html_url: payload.object_attributes.url)) + .take + end + + def upsert_issue(work_packages) + return if work_packages.empty? && gitlab_issue.nil? + OpenProject::GitlabIntegration::Services::UpsertIssue.new.call(payload, + work_packages: work_packages) + end end end end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb index fff9432bee6a..c4e65d055724 100644 --- a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb @@ -33,7 +33,7 @@ module NotificationHandler # Handles Gitlab merge request notifications. class MergeRequestHook include OpenProject::GitlabIntegration::NotificationHandler::Helper - + def process(payload_params) update_status_on_new_mr = false # true if you only reference one merge by work_package, else false. update_status_on_merged = false # true if you only reference one merge by work_package, else false. @@ -48,10 +48,10 @@ def process(payload_params) return unless (accepted_actions.include? payload.object_attributes.action) || (accepted_states.include? payload.object_attributes.state) user = User.find_by_id(payload.open_project_user_id) - text = payload.object_attributes.title + text = payload.object_attributes.title + ' - ' + payload.object_attributes.description work_packages = find_mentioned_work_packages(text, user) notes = generate_notes(payload) - + if (accepted_actions_for_comments.include? payload.object_attributes.action) || (accepted_states.include? payload.object_attributes.state) comment_on_referenced_work_packages(work_packages, user, notes) if payload.object_attributes.state == 'opened' && update_status_on_new_mr @@ -76,7 +76,7 @@ def generate_notes(payload) 'edited' => 'referenced', 'referenced' => 'referenced' }[payload.object_attributes.state] - + key_action = { 'reopen' => 'reopened' }[payload.object_attributes.action] @@ -108,4 +108,4 @@ def upsert_merge_request(work_packages) end end -end \ No newline at end of file +end diff --git a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb index baf7ca8db2b4..1c14b01fb171 100644 --- a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb @@ -42,10 +42,10 @@ def process(payload_params) return unless merge_request # disabled until gitlab issue resolution - # OpenProject::GitlabIntegration::Services::UpsertPipeline.new.call( - # payload, - # merge_request: merge_request - # ) + OpenProject::GitlabIntegration::Services::UpsertPipeline.new.call( + payload, + merge_request: merge_request + ) end private diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb index 7a67f36f5d55..0c2a8cf1c095 100644 --- a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb +++ b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb @@ -52,6 +52,15 @@ def extension title: "Gitlab merge requests" } end + + link :gitlab_issues do + next if represented.new_record? + + { + href: api_v3_paths.gitlab_issues_by_work_package(represented.id), + title: "Gitlab Issues" + } + end end end end diff --git a/lib/open_project/gitlab_integration/patches/work_package_patch.rb b/lib/open_project/gitlab_integration/patches/work_package_patch.rb index eabf7ea5d07c..833b51b908ab 100644 --- a/lib/open_project/gitlab_integration/patches/work_package_patch.rb +++ b/lib/open_project/gitlab_integration/patches/work_package_patch.rb @@ -5,6 +5,7 @@ module WorkPackagePatch included do has_and_belongs_to_many :gitlab_merge_requests + has_and_belongs_to_many :gitlab_issues end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/lib/open_project/gitlab_integration/services/upsert_issue.rb new file mode 100644 index 000000000000..402e6bee5c02 --- /dev/null +++ b/lib/open_project/gitlab_integration/services/upsert_issue.rb @@ -0,0 +1,84 @@ +#-- encoding: UTF-8 + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject::GitlabIntegration::Services + ## + # Takes merge request data coming from Gitlab webhook data and stores + # them as a `GitlabMergeRequest`. + # If the `GitlabMergeRequest` already exists, it is updated. + # + # Returns the upserted `GitlabMergeRequest`. + class UpsertIssue + def call(payload, work_packages: []) + find_or_initialize(payload).tap do |issue| + issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) + end + end + + private + + def find_or_initialize(payload) + GitlabIssue.find_by_gitlab_identifiers(id: payload.object_attributes.iid, + url: payload.object_attributes.url, + initialize: true) + end + + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + # rubocop:disable Metrics/AbcSize + def extract_params(payload) + { + gitlab_id: payload.object_attributes.iid, + gitlab_user: gitlab_user_id(payload.user), + number: payload.object_attributes.iid, + gitlab_html_url: payload.object_attributes.url, + gitlab_updated_at: payload.object_attributes.updated_at, + state: payload.object_attributes.state, + title: payload.object_attributes.title, + body: payload.object_attributes.description, + repository: payload.repository.name, + labels: payload.labels.map { |values| extract_label_values(values) } + } + end + # rubocop:enable Metrics/AbcSize + + def extract_label_values(payload) + { + title: payload['title'], + color: payload['color'] + } + end + + def gitlab_user_id(payload) + return if payload.blank? + + ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + end + end +end diff --git a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb index fdca7fc2a1e0..ed91fce2190c 100644 --- a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb +++ b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb @@ -36,7 +36,7 @@ module OpenProject::GitlabIntegration::Services # Returns the upserted `GitlabPipeline`. class UpsertPipeline def call(payload, merge_request:) - GitlabPipelines.find_or_initialize_by(gitlab_id: payload.object_attributes.id) + GitlabPipeline.find_or_initialize_by(gitlab_id: payload.object_attributes.iid) .tap do |pipeline| pipeline.update!(gitlab_merge_request: merge_request, **extract_params(payload)) end @@ -48,14 +48,16 @@ def call(payload, merge_request:) # to our internal representation. def extract_params(payload) { - gitlab_id: payload.object_attributes.id, - gitlab_html_url: payload.project.web_url + "-/pipelines/" + payload.object_attributes.id, + gitlab_id: payload.object_attributes.iid, + gitlab_html_url: "#{payload.project.web_url}/-/pipelines/#{payload.object_attributes.iid}", project_id: payload.project.id, gitlab_user_avatar_url: payload.user.avatar_url, - name: payload.object_attributes.status, + name: payload.object_attributes.iid, status: payload.object_attributes.status, - details_url: payload.project.web_url + "-/pipelines/" + payload.object_attributes.id, - # ci_details: pending until resolution of the gitlab issue, + details_url: "#{payload.project.web_url}/-/commit/#{payload.object_attributes.sha[0..7]}", + commit_id: payload.object_attributes.sha[0..7], + username: payload.user.name, + ci_details: payload.builds, started_at: payload.object_attributes.created_at, completed_at: payload.object_attributes.finished_at } diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 41ddb2632a40..013dae3603ef 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -1,5 +1,5 @@ # encoding: UTF-8 -# +# #-- copyright # OpenProject is an open source project management software. # Copyright (C) 2023 Ben Tey @@ -31,7 +31,7 @@ Gem::Specification.new do |s| s.name = "openproject-gitlab_integration" - s.version = '2.0.9' + s.version = '2.1' s.authors = "Ben Tey" s.email = "ben.tey@outlook.com" s.homepage = "https://github.com/btey/openproject-gitlab-integration" From 75e2fcdb8e2689e04bfa325af77172c00029d7dc Mon Sep 17 00:00:00 2001 From: btey Date: Mon, 20 Nov 2023 14:17:00 -0500 Subject: [PATCH 36/69] Update readme with details for v2.1 --- README.md | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 21d9fe0b6d44..e91811673ce8 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,25 @@ -# openproject-gitlab-integration +## Introducing OpenProject GitLab Integration v2.1 GA -## NEW VERSION 2.0.6 GA +Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). -Based on the current Github integration (OpenProject 12), this plugin offers the same functionalities as the current plugin for Github (and something else). This version includes changes to the DB and a new view similar to the current Github tab. Only the management of "pipelines" is pending an open issue in Gitlab (https://gitlab.com/gitlab-org/gitlab/-/issues/345028). +In this version it has also been implemented that **all linked or referenced** Issues appear in the GitLab tab (https://github.com/btey/openproject-gitlab-integration/issues/34). The opportunity has also been taken to redesign how the information is presented so that it is visually easy to read and at the same time can continue to provide all the information, including labels and pipeline status. -![OP-Gitlab](https://user-images.githubusercontent.com/14983519/143622798-13d1c50b-1186-46e0-a2da-9f50fb14a338.png) +If there are labels related to the Issue or MR, a button with the label icon will appear. By clicking the button you can show/hide the associated labels. -This 2.x version includes a UI with all linked MRs, their status, their labels and the last pipeline *(pending the Gitlab issue)*. -## Introduction + -OpenProject module for integration with Gitlab: -* Latest Gitlab release tested: **15.3.1** -* Latest OpenProject release tested: **12.2.1** (for OpenProject versions earlier than 12.2.0 use v2.0.5, and for version earlier than 12.1.0 use v2.0.4) + -This plugin is based on the current [plugin to integrate Github with OpenProject](https://www.openproject.org/docs/system-admin-guide/integrations/github-integration/). -The reference system is the same as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title in Gitlab. +## Overview -> **Note about the references.** Whether or not to include the reference in certain places depends on the information that Gitlab sends through its webhook. If you include the reference in the title of an issue, the comments on the issue do not need to include the reference. The same will happen when you generate a Merge Request based on an Issue that already includes the reference; comments from that MR need not include the reference. +OpenProject module for integration with GitLab: +* Latest Gitlab release tested: **16.5.1** +* Latest OpenProject release tested: **13.0.7** + +The reference system is based on the same system as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title/description of the Issue/MR in GitLab. + +> **Note about the references.** Whether or not to include the reference in certain places depends on the information that GitLab sends through its webhook. If you include the reference in the title/description of an issue, the comments on the issue do not need to include the reference. The same will happen when you generate a Merge Request based on an Issue that already includes the reference; comments from that MR need not include the reference. #### Difference between OP and PP @@ -31,7 +33,7 @@ OpenProject will **add comments** to work package for the following events: * Issue (Opened, Closed) * Push commits in Merge Requests * Comments (on Issues, Merge Request, Commits and Snippets) -* *Pipelines (pending)* +* Pipelines (Beta feature) OpenProject will **update WP status** in this events: @@ -42,7 +44,7 @@ OpenProject will **update WP status** in this events: ## Example workflow -A typical workflow on Gitlab side would be: +A typical workflow on GitLab side would be: 1. **Create Issue.** @@ -157,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.0.6) + openproject-gitlab_integration (2.1) openproject-webhooks ``` @@ -190,7 +192,7 @@ bundle install --deployment --without mysql2 sqlite development test therubyrace bundle config set deployment ``` -### The Gitlab Bot user in OpenProject +### The GitLab Bot user in OpenProject First you will need to create a user in OpenProject that will make the comments. The user will have to be added to each project with a role that allows them to comment on work packages and change status. @@ -202,13 +204,13 @@ Once the user is created you need to generate an OpenProject API token for it to * Click on generate in the API row. * Copy the generated key. You can now configure the necessary webhook in Gitlab. -### The webhook in Gitlab +### The webhook in GitLab -In Gitlab you have to set up a webhook in each repository to be integrated with OpenProject. +In GitLab you have to set up a webhook in each repository to be integrated with OpenProject. You need to configure just two things in the webhook: -1. The URL must point to your OpenProject server’s Gitlab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: +1. The URL must point to your OpenProject server’s GitLab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: ``` http://openproject-url.com/webhooks/gitlab?key=ae278268 @@ -223,10 +225,12 @@ You need to configure just two things in the webhook: 3. Issues events 4. Merge request events + + 5. Pipeline events Now the integration is set up on both sides and you can use it. -> **Note:** If you are installing and configuring OpenProject on the same server as Gitlab you will need to enable in Gitlab the option "Allow requests to the local network from web hooks and services" so that it can send the data locally to the OpenProject webhook since they will be on the same machine. +> **Note:** If you are installing and configuring OpenProject on the same server as GitLab you will need to enable in Gitlab the option "Allow requests to the local network from web hooks and services" so that it can send the data locally to the OpenProject webhook since they will be on the same machine. ## How to report bugs or issues From 81a2ef3d607d38d34cdd14be8884f1e8d9fe1f19 Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 21 Nov 2023 20:23:57 +0000 Subject: [PATCH 37/69] add gitlab documentation links + minor improvments --- README.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index e91811673ce8..318595cde4af 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ A typical workflow on GitLab side would be: You will have to configure both **OpenProject** and **Gitlab** for the integration to work. In case of **Docker** installation, follow the official OpenProject documentation [here](https://www.openproject.org/docs/installation-and-operations/installation/docker/#openproject-plugins). If for some reason the installation with Docker described in the official documentation does not work for you, you can try building your own docker image: -* Clone from the Openproject Repo: `git clone https://github.com/opf/openproject.git --branch=stable/12 --depth=1 .` +* Clone from the Openproject Repo: `git clone https://github.com/opf/openproject.git --branch=stable/13 --depth=1 .` * Clone the plugin inside the modules folder: `git clone https://github.com/btey/openproject-gitlab-integration.git --depth=1 modules/gitlab_integration` * Apply the changes below in Gemfile.lock and Gemfile.modules (the same ones you would do in a manual install). * Build the container: `docker build -t openproject-docker --file=docker/prod/Dockerfile .` @@ -155,7 +155,7 @@ But first you must modify **Gemfile.lock** and **Gemfile.modules** so that OpenP Add the following in **Gemfile.lock**: -``` +```yml PATH remote: modules/gitlab_integration specs: @@ -165,7 +165,7 @@ PATH And add this other line in DEPENDENCIES section: -``` +```yml DEPENDENCIES ... openproject-github_integration! @@ -176,7 +176,7 @@ DEPENDENCIES Add the following in **Gemfile.modules**: -``` +```yml group :opf_plugins do ... gem 'openproject-gitlab_integration', path: 'modules/gitlab_integration' @@ -206,7 +206,7 @@ Once the user is created you need to generate an OpenProject API token for it to ### The webhook in GitLab -In GitLab you have to set up a webhook in each repository to be integrated with OpenProject. +In GitLab you have to set up a webhook in each project or in a group ([Premium Users](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#group-webhooks)) to be integrated with OpenProject. You need to configure just two things in the webhook: @@ -216,21 +216,16 @@ You need to configure just two things in the webhook: http://openproject-url.com/webhooks/gitlab?key=ae278268 ``` -2. Enable the required triggers: - - 1. Push events - - 2. Comments - +2. Enable the required triggers: + 1. Push events + 2. Comments 3. Issues events - - 4. Merge request events - + 4. Merge request events 5. Pipeline events Now the integration is set up on both sides and you can use it. -> **Note:** If you are installing and configuring OpenProject on the same server as GitLab you will need to enable in Gitlab the option "Allow requests to the local network from web hooks and services" so that it can send the data locally to the OpenProject webhook since they will be on the same machine. +> **Note:** If you are installing and configuring OpenProject on the same server as GitLab you will need to enable in Gitlab the option [`Allow requests to the local network from web hooks and services`](https://docs.gitlab.com/ee/security/webhooks.html#allow-requests-to-the-local-network-from-webhooks-and-integrations) so that it can send the data locally to the OpenProject webhook since they will be on the same machine. ## How to report bugs or issues From 389066ad65d52beb7bc568ce317545422b7637bb Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 21 Nov 2023 20:32:13 +0000 Subject: [PATCH 38/69] add link for configure-a-webhook-in-gitlab --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 318595cde4af..47c443d8d0d5 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ Once the user is created you need to generate an OpenProject API token for it to ### The webhook in GitLab -In GitLab you have to set up a webhook in each project or in a group ([Premium Users](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#group-webhooks)) to be integrated with OpenProject. +In GitLab you have to [set up a webhook](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#configure-a-webhook-in-gitlab) in each project or in a group ([Premium Users](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#group-webhooks)) to be integrated with OpenProject. You need to configure just two things in the webhook: From 04a155d01132afb75914b6bf1dc2f2e8ceb9321a Mon Sep 17 00:00:00 2001 From: Marc Date: Tue, 21 Nov 2023 20:33:40 +0000 Subject: [PATCH 39/69] syntax highlighting for shell commands --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 47c443d8d0d5..8a13c9dd4e7a 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ end **Note:** It's possible that you need to use these commands before and after the "bundle install" if you get an error in this step warning about a change in the Gemfile: -``` +```shell bundle config unset deployment bundle install --deployment --without mysql2 sqlite development test therubyracer docker bundle config set deployment From 8859ff87535b5ac61a9f33e296e2b482cda811af Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Thu, 23 Nov 2023 22:41:54 +0300 Subject: [PATCH 40/69] russian locale translated --- config/locales/crowdin/js-ru.yml | 34 ++++++------- config/locales/crowdin/ru.yml | 84 ++++++++++++++++---------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index a71c435d49db..9e612d36e9ef 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -30,27 +30,27 @@ ru: js: gitlab_integration: work_packages: - tab_name: "GitLab" + tab_name: GitLab tab_header_issue: - title: "Issues" + title: Обсуждения tab_header_mr: - title: "Merge requests" + title: Запросы на слияние create_mr: - label: Create MR - description: Create a Merge Request + label: Создать запрос на слияние + description: Создать запрос на слияние copy_menu: - label: Git snippets - description: Copy git snippets to clipboard + label: Git-сниппеты + description: Скопипастить Git-сниппеты git_actions: - branch_name: Branch name - commit_message: Commit message - cmd: Create branch with empty commit - title: Quick snippets for Git - copy_success: ✅ Copied! - copy_error: ❌ Copy failed! + branch_name: Имя ветки + commit_message: Описание коммита + cmd: Создать ветку с пустым коммитом + title: Быстрые сниппеты для Git + copy_success: ✅ Скопировано + copy_error: ❌ Не получилось скопировать tab_issue: - empty: There are no issues linked yet. Link an existing issue by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the issue title/description or create a new issue. + empty: Нет связанных обсуждений. Привязать обсуждение можно путём вставки кода OP#%{wp_id} (или PP#%{wp_id} для приватных ссылок) в заголовке/описании при создании/редактировании обсуждения. tab_mrs: - empty: There are no merge requests linked yet. Link an existing MR by using the code OP#%{wp_id} (or PP#%{wp_id} for private links) in the MR title/description or create a new MR. - gitlab_pipelines: Pipelines - updated_on: Updated on + empty: Нет связанных запросов на слияние. Привязать запрос на слияние можно путём вставки кода OP#%{wp_id} (или PP#%{wp_id} для приватных ссылок) в заголовке/описании при создании/редактировании запроса на слияние. ОБЯЗАТЕЛЬНО заполните описание! + gitlab_pipelines: Сборочные линии (pipelines) + updated_on: Обновлено diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index b17b09dc0d2b..2b3182a0af00 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -27,67 +27,67 @@ # See docs/COPYRIGHT.rdoc for more details. #++ ru: - project_module_gitlab: "Gitlab" - permission_show_gitlab_content: "Show Gitlab content" + project_module_gitlab: GitLab + permission_show_gitlab_content: Показывать GitLab-контент gitlab_integration: merge_request_opened_comment: > - **MR Opened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) - has been opened by [%{gitlab_user}](%{gitlab_user_url}). - merge_request_closed_comment: > - **MR Closed:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) - has been closed by [%{gitlab_user}](%{gitlab_user_url}). + **MR Открыт:** Запрос на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}) + был открыт [%{gitlab_user}](%{gitlab_user_url}). + merge_request_закрыт_comment: > + **MR Закрыт:** Запрос на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}) + был закрыт by [%{gitlab_user}](%{gitlab_user_url}). merge_request_merged_comment: > - **MR Merged:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) - has been merged by [%{gitlab_user}](%{gitlab_user_url}). + **MR Смержен:** Запрос на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}) + был смержен [%{gitlab_user}](%{gitlab_user_url}). merge_request_reopened_comment: > - **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) - has been reopened by [%{gitlab_user}](%{gitlab_user_url}). + **MR Переоткрыт:** Запрос на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}) + был переоткрыт [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > - **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in - a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): - + **Упомянут в коммите:** [%{gitlab_user}](%{gitlab_user_url}) отметил эту задачу в + a коммите [%{commit_id}](%{commit_url}) в [%{repository}](%{repository_url}): + %{commit_note} note_mr_referenced_comment: > - **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in - Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): - + **Упомянут в MR:** [%{gitlab_user}](%{gitlab_user_url}) отметил эту задачу в + запросе на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}): + %{mr_note} note_mr_commented_comment: > - **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in - Merge request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): - + **Комментарий в MR:** [%{gitlab_user}](%{gitlab_user_url}) комментировал эту задачу в + запросе на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}): + %{mr_note} note_issue_referenced_comment: > - **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in - Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): - + **Упомянут в обсуждении:** [%{gitlab_user}](%{gitlab_user_url}) отметил эту задачу в + Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}): + %{issue_note} note_issue_commented_comment: > - **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in - Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): - + **Комментарий в обсуждении:** [%{gitlab_user}](%{gitlab_user_url}) комментировал эту задачу в + обсуждении %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}): + %{issue_note} note_snippet_referenced_comment: > - **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in - Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): - + **Упомянут в Snippet:** [%{gitlab_user}](%{gitlab_user_url}) отметил эту задачу в + Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) в [%{repository}](%{repository_url}): + %{snippet_note} issue_opened_referenced_comment: > - **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) - has been opened by [%{gitlab_user}](%{gitlab_user_url}). - issue_closed_referenced_comment: > - **Issue Closed:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) - has been closed by [%{gitlab_user}](%{gitlab_user_url}). + **Обсуждение открыто:** Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}) + был открыт [%{gitlab_user}](%{gitlab_user_url}). + issue_закрыт_referenced_comment: > + **Обсуждение закрыто:** Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}) + был закрыт [%{gitlab_user}](%{gitlab_user_url}). issue_reopened_referenced_comment: > - **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) - has been reopened by [%{gitlab_user}](%{gitlab_user_url}). + **Обсуждение переоткрыто:** Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}) + было переоткрыто [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > - **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) - to [%{repository}](%{repository_url}) at %{commit_timestamp}: - + **Коммит в MR:** [%{gitlab_user}](%{gitlab_user_url}) закоммитил [%{commit_number}](%{commit_url}) + в [%{repository}](%{repository_url}) %{commit_timestamp}: + %{commit_note} push_multiple_commits_comment: > - **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) - to [%{repository}](%{repository_url}) at %{commit_timestamp}: - + **Коммит в MR:** [%{gitlab_user}](%{gitlab_user_url}) закоммитил несколько коммитов [%{commit_number}](%{commit_url}) + в [%{repository}](%{repository_url}) %{commit_timestamp}: + %{commit_note} From d42eed2e12e3dfc9b0017d5ade6ceb29824745e1 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Thu, 23 Nov 2023 22:45:58 +0300 Subject: [PATCH 41/69] snippet for commit message contained wrong task code --- frontend/module/git-actions/git-actions.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/module/git-actions/git-actions.service.ts b/frontend/module/git-actions/git-actions.service.ts index 1c87adb9c1fc..27688d8f8fbb 100644 --- a/frontend/module/git-actions/git-actions.service.ts +++ b/frontend/module/git-actions/git-actions.service.ts @@ -73,7 +73,7 @@ export class GitActionsService { public commitMessage(workPackage:WorkPackageResource):string { const { title, id, description, url } = this.formattingInput(workPackage); - return `[#${id}] ${title} + return `OP#${id} ${title} ${description} From 03fb32d8b053920d9b90671142c216f04f554831 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Mon, 27 Nov 2023 21:52:39 +0300 Subject: [PATCH 42/69] russian locale fix --- config/locales/crowdin/ru.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 2b3182a0af00..50cc9f2b2e6c 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -33,7 +33,7 @@ ru: merge_request_opened_comment: > **MR Открыт:** Запрос на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}) был открыт [%{gitlab_user}](%{gitlab_user_url}). - merge_request_закрыт_comment: > + merge_request_closed_comment: > **MR Закрыт:** Запрос на слияние %{mr_number} [%{mr_title}](%{mr_url}) в [%{repository}](%{repository_url}) был закрыт by [%{gitlab_user}](%{gitlab_user_url}). merge_request_merged_comment: > From 419f94b28584d6657658a4704b4293636f14e365 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Fri, 1 Dec 2023 12:51:44 +0300 Subject: [PATCH 43/69] fix 500 errors with empty MR and Issue description body, fix empty avatar_url --- .../services/upsert_gitlab_user.rb | 64 +++++----- .../services/upsert_issue.rb | 102 ++++++++-------- .../services/upsert_merge_request.rb | 110 ++++++++++-------- openproject-gitlab_integration.gemspec | 17 +-- 4 files changed, 160 insertions(+), 133 deletions(-) diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb index 42845f14e16d..87aaac7d5a12 100644 --- a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb +++ b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb @@ -1,4 +1,4 @@ -#-- encoding: UTF-8 +# frozen_string_literal: true #-- copyright # OpenProject is an open source project management software. @@ -27,34 +27,44 @@ # # See docs/COPYRIGHT.rdoc for more details. #++ -module OpenProject::GitlabIntegration::Services - ## - # Takes user data coming from Gitlab webhook data and stores - # them as a `GitlabUser`. - # If the `GitlabUser` already exists, it is updated. - # - # Returns the upserted `GitlabUser`. - class UpsertGitlabUser - def call(payload) - GitlabUser.find_or_initialize_by(gitlab_id: payload.id) - .tap do |gitlab_user| - gitlab_user.update!(extract_params(payload)) - end - end +module OpenProject + module GitlabIntegration + module Services + ## + # Takes user data coming from Gitlab webhook data and stores + # them as a `GitlabUser`. + # If the `GitlabUser` already exists, it is updated. + # + # Returns the upserted `GitlabUser`. + class UpsertGitlabUser + def call(payload) + GitlabUser.find_or_initialize_by(gitlab_id: payload.id) + .tap do |gitlab_user| + gitlab_user.update!(extract_params(payload)) + end + end + + private - private + ## + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + def extract_params(payload) + { + gitlab_id: payload.id, + gitlab_name: payload.name, + gitlab_username: payload.username, + gitlab_email: payload.email, + gitlab_avatar_url: avatar_url(payload) + } + end - ## - # Receives the input from the gitlab webhook and translates them - # to our internal representation. - def extract_params(payload) - { - gitlab_id: payload.id, - gitlab_name: payload.name, - gitlab_username: payload.username, - gitlab_email: payload.email, - gitlab_avatar_url: payload.avatar_url - } + def avatar_url(payload) + # rubocop:disable Layout/LineLength + payload.avatar_url.presence || "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M12 2.5a5.5 5.5 0 0 1 3.096 10.047 9.005 9.005 0 0 1 5.9 8.181.75.75 0 1 1-1.499.044 7.5 7.5 0 0 0-14.993 0 .75.75 0 0 1-1.5-.045 9.005 9.005 0 0 1 5.9-8.18A5.5 5.5 0 0 1 12 2.5ZM8 8a4 4 0 1 0 8 0 4 4 0 0 0-8 0Z'%3E%3C/path%3E%3C/svg%3E" + # rubocop:enable Layout/LineLength + end + end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/lib/open_project/gitlab_integration/services/upsert_issue.rb index 402e6bee5c02..969db32a786c 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue.rb @@ -1,4 +1,4 @@ -#-- encoding: UTF-8 +# frozen_string_literal: true #-- copyright # OpenProject is an open source project management software. @@ -27,58 +27,66 @@ # # See docs/COPYRIGHT.rdoc for more details. #++ -module OpenProject::GitlabIntegration::Services - ## - # Takes merge request data coming from Gitlab webhook data and stores - # them as a `GitlabMergeRequest`. - # If the `GitlabMergeRequest` already exists, it is updated. - # - # Returns the upserted `GitlabMergeRequest`. - class UpsertIssue - def call(payload, work_packages: []) - find_or_initialize(payload).tap do |issue| - issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) - end - end +module OpenProject + module GitlabIntegration + module Services + ## + # Takes merge request data coming from Gitlab webhook data and stores + # them as a `GitlabMergeRequest`. + # If the `GitlabMergeRequest` already exists, it is updated. + # + # Returns the upserted `GitlabMergeRequest`. + class UpsertIssue + def call(payload, work_packages: []) + find_or_initialize(payload).tap do |issue| + issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) + end + end - private + private - def find_or_initialize(payload) - GitlabIssue.find_by_gitlab_identifiers(id: payload.object_attributes.iid, - url: payload.object_attributes.url, - initialize: true) - end + def find_or_initialize(payload) + GitlabIssue.find_by_gitlab_identifiers(id: payload.object_attributes.iid, + url: payload.object_attributes.url, + initialize: true) + end - # Receives the input from the gitlab webhook and translates them - # to our internal representation. - # rubocop:disable Metrics/AbcSize - def extract_params(payload) - { - gitlab_id: payload.object_attributes.iid, - gitlab_user: gitlab_user_id(payload.user), - number: payload.object_attributes.iid, - gitlab_html_url: payload.object_attributes.url, - gitlab_updated_at: payload.object_attributes.updated_at, - state: payload.object_attributes.state, - title: payload.object_attributes.title, - body: payload.object_attributes.description, - repository: payload.repository.name, - labels: payload.labels.map { |values| extract_label_values(values) } - } - end - # rubocop:enable Metrics/AbcSize + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + # rubocop:disable Metrics/AbcSize + def extract_params(payload) + { + gitlab_id: payload.object_attributes.iid, + gitlab_user: gitlab_user_id(payload.user), + number: payload.object_attributes.iid, + gitlab_html_url: payload.object_attributes.url, + gitlab_updated_at: payload.object_attributes.updated_at, + state: payload.object_attributes.state, + title: payload.object_attributes.title, + body: description(payload), + repository: payload.repository.name, + labels: payload.labels.map { |values| extract_label_values(values) } + } + end + # rubocop:enable Metrics/AbcSize - def extract_label_values(payload) - { - title: payload['title'], - color: payload['color'] - } - end + def extract_label_values(payload) + { + title: payload['title'], + color: payload['color'] + } + end + + def gitlab_user_id(payload) + return if payload.blank? - def gitlab_user_id(payload) - return if payload.blank? + ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + end - ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + def description(payload) + payload.object_attributes.description.presence || 'No description provided' + end + end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb index d67a17e27012..a81176a269b1 100644 --- a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb +++ b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb @@ -1,4 +1,4 @@ -#-- encoding: UTF-8 +# frozen_string_literal: true #-- copyright # OpenProject is an open source project management software. @@ -27,62 +27,70 @@ # # See docs/COPYRIGHT.rdoc for more details. #++ -module OpenProject::GitlabIntegration::Services - ## - # Takes merge request data coming from Gitlab webhook data and stores - # them as a `GitlabMergeRequest`. - # If the `GitlabMergeRequest` already exists, it is updated. - # - # Returns the upserted `GitlabMergeRequest`. - class UpsertMergeRequest - def call(payload, work_packages: []) - find_or_initialize(payload).tap do |mr| - mr.update!(work_packages: mr.work_packages | work_packages, **extract_params(payload)) - end - end +module OpenProject + module GitlabIntegration + module Services + ## + # Takes merge request data coming from Gitlab webhook data and stores + # them as a `GitlabMergeRequest`. + # If the `GitlabMergeRequest` already exists, it is updated. + # + # Returns the upserted `GitlabMergeRequest`. + class UpsertMergeRequest + def call(payload, work_packages: []) + find_or_initialize(payload).tap do |mr| + mr.update!(work_packages: mr.work_packages | work_packages, **extract_params(payload)) + end + end - private + private - def find_or_initialize(payload) - GitlabMergeRequest.find_by_gitlab_identifiers(id: payload.object_attributes.iid, - url: payload.object_attributes.url, - initialize: true) - end + def find_or_initialize(payload) + GitlabMergeRequest.find_by_gitlab_identifiers(id: payload.object_attributes.iid, + url: payload.object_attributes.url, + initialize: true) + end - # Receives the input from the gitlab webhook and translates them - # to our internal representation. - # rubocop:disable Metrics/AbcSize - def extract_params(payload) - { - gitlab_id: payload.object_attributes.iid, - gitlab_user: gitlab_user_id(payload.user), - number: payload.object_attributes.iid, - gitlab_html_url: payload.object_attributes.url, - gitlab_updated_at: payload.object_attributes.updated_at, - state: payload.object_attributes.state, - title: payload.object_attributes.title, - body: payload.object_attributes.description, - repository: payload.repository.name, - draft: payload.object_attributes.work_in_progress, - merged: payload.object_attributes.state == 'merged' ? true : false, - merged_by: gitlab_user_id(payload.user), - merged_at: payload.object_attributes.state == 'merged' ? payload.object_attributes.updated_at : nil, - labels: payload.labels.map { |values| extract_label_values(values) } - } - end - # rubocop:enable Metrics/AbcSize + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + # rubocop:disable Metrics/AbcSize + def extract_params(payload) + { + gitlab_id: payload.object_attributes.iid, + gitlab_user: gitlab_user_id(payload.user), + number: payload.object_attributes.iid, + gitlab_html_url: payload.object_attributes.url, + gitlab_updated_at: payload.object_attributes.updated_at, + state: payload.object_attributes.state, + title: payload.object_attributes.title, + body: description(payload), + repository: payload.repository.name, + draft: payload.object_attributes.work_in_progress, + merged: payload.object_attributes.state == 'merged', + merged_by: gitlab_user_id(payload.user), + merged_at: payload.object_attributes.state == 'merged' ? payload.object_attributes.updated_at : nil, + labels: payload.labels.map { |values| extract_label_values(values) } + } + end + # rubocop:enable Metrics/AbcSize - def extract_label_values(payload) - { - title: payload['title'], - color: payload['color'] - } - end + def extract_label_values(payload) + { + title: payload['title'], + color: payload['color'] + } + end + + def gitlab_user_id(payload) + return if payload.blank? - def gitlab_user_id(payload) - return if payload.blank? + ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + end - ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + def description(payload) + payload.object_attributes.description.presence || 'No description provided' + end + end end end end diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 013dae3603ef..9c4abf49d3a6 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -1,4 +1,5 @@ -# encoding: UTF-8 +# frozen_string_literal: true + # #-- copyright # OpenProject is an open source project management software. @@ -30,16 +31,16 @@ #++ Gem::Specification.new do |s| - s.name = "openproject-gitlab_integration" - s.version = '2.1' - s.authors = "Ben Tey" - s.email = "ben.tey@outlook.com" - s.homepage = "https://github.com/btey/openproject-gitlab-integration" + s.name = 'openproject-gitlab_integration' + s.version = '2.1.1' + s.authors = 'Ben Tey' + s.email = 'ben.tey@outlook.com' + s.homepage = 'https://github.com/btey/openproject-gitlab-integration' s.summary = 'OpenProject GitLab Integration' s.description = 'Integrates OpenProject and GitLab for a better workflow' s.license = 'GPLv3' - s.files = Dir["{app,config,db,frontend,lib}/**/*"] + %w(README.md) + s.files = Dir['{app,config,db,frontend,lib}/**/*'] + %w[README.md] - s.add_dependency "openproject-webhooks" + s.add_dependency 'openproject-webhooks' end From 8554e43a26f0874eb1dc700533330707911a180f Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Fri, 1 Dec 2023 12:59:14 +0300 Subject: [PATCH 44/69] replaced avatar_url from SVG to URL --- .../gitlab_integration/services/upsert_gitlab_user.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb index 87aaac7d5a12..fcc9bea55438 100644 --- a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb +++ b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb @@ -60,9 +60,7 @@ def extract_params(payload) end def avatar_url(payload) - # rubocop:disable Layout/LineLength - payload.avatar_url.presence || "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath d='M12 2.5a5.5 5.5 0 0 1 3.096 10.047 9.005 9.005 0 0 1 5.9 8.181.75.75 0 1 1-1.499.044 7.5 7.5 0 0 0-14.993 0 .75.75 0 0 1-1.5-.045 9.005 9.005 0 0 1 5.9-8.18A5.5 5.5 0 0 1 12 2.5ZM8 8a4 4 0 1 0 8 0 4 4 0 0 0-8 0Z'%3E%3C/path%3E%3C/svg%3E" - # rubocop:enable Layout/LineLength + payload.avatar_url.presence || 'https://www.gravatar.com/avatar/?d=mp' end end end From 8c86bf371777147189afa00e19a18fbd3f6abb2c Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Fri, 1 Dec 2023 13:18:59 +0300 Subject: [PATCH 45/69] russian locale fix --- config/locales/crowdin/ru.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 50cc9f2b2e6c..3da85dbd8109 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -75,7 +75,7 @@ ru: issue_opened_referenced_comment: > **Обсуждение открыто:** Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}) был открыт [%{gitlab_user}](%{gitlab_user_url}). - issue_закрыт_referenced_comment: > + issue_closed_referenced_comment: > **Обсуждение закрыто:** Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}) был закрыт [%{gitlab_user}](%{gitlab_user_url}). issue_reopened_referenced_comment: > From bdf945272ecac792f70d182c356ffd85d0c3c036 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Fri, 1 Dec 2023 17:24:41 +0300 Subject: [PATCH 46/69] openproject-gitlab-integration README.md updated --- README.md | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 8a13c9dd4e7a..0a497bf5b5d4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1 GA +## Introducing OpenProject GitLab Integration v2.1.1 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -15,7 +15,7 @@ If there are labels related to the Issue or MR, a button with the label icon wil OpenProject module for integration with GitLab: * Latest Gitlab release tested: **16.5.1** -* Latest OpenProject release tested: **13.0.7** +* Latest OpenProject release tested: **13.0.8** The reference system is based on the same system as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title/description of the Issue/MR in GitLab. @@ -47,89 +47,89 @@ OpenProject will **update WP status** in this events: A typical workflow on GitLab side would be: 1. **Create Issue.** - + - + > **Issue Opened:** Issue 6 New contact form - OP#18 for Scrum project has been opened by Administrator. 2. **Comment on issue.** - + If the reference is included in the title, the comments will not need a reference. By default, all comments will use the title as a reference. - + - + > **Commented in Issue:** Administrator commented this WP in Issue 6 New contact form - OP#18 on Scrum project: - > + > > New comment on the issue with attachment. 3. **Create Merge Request.** - + - + > **MR Opened:** Merge request 25 Draft: Resolve "New contact form - OP#18" for Scrum project has been opened by Administrator. - > + > > **Status** changed from _Specified_ > **to** _In progress_ 4. **Comment in Merge Request.** - + - + > **Commented in MR:** Administrator commented this WP in Merge request 25 Draft: Resolve "New contact form - OP#18" on Scrum project: - > + > > New comment on MR. 5. **Reference in other Issues or Merge Request (comments).** - + If the reference is NOT included in the title of the Issue/MR, the comments will need a reference. In OpenProject the comment will be saved as "referenced" in Issue/MR. - + - + > **Referenced in Issue:** Administrator referenced this WP in Issue 2 New backend pipeline on Scrum project: - > + > > OP#18 New comment about... - > - > **Note:** If you use the reference `PP#` in the title of the Issue/MR, you can use `OP#` in the comment to generate the same type of comment in OpenProject. + > + > **Note:** If you use the reference `PP#` in the title of the Issue/MR, you can use `OP#` in the comment to generate the same type of comment in OpenProject. 6. **New commit in Merge Request.** - + - + > **Pushed in MR:** Administrator pushed fca3d6fb to Scrum project at 2021-03-08T08:01:57+00:00: - > + > > Update readme.md OP#18 7. **Comment in a new commit of the Merge Request.** - + - + > **Referenced in Commit:** Administrator referenced this WP in a Commit Note 0bf0e3e9 on Scrum project: - > + > > This change is for OP#18. 8. **Merge Request merged (generates up to 3 events).** - + - + > **Pushed in MR:** Administrator pushed 1da09cb4 to Scrum project at 2021-03-05T14:57:37+00:00: - > + > > Merge branch '5-new-contact-form-op-18' into 'master' - > + > > Resolve "New contact form - OP#18" - > + > > Closes #6 - > + > > See merge request root/scrum!9 - + - + > **MR Merged:** Merge request 24 Resolve "New contact form - OP#18" for Scrum project has been merged by Administrator. - > + > > **Status** changed from _In progress_ > **to** _Developed_ - + - + > **Issue Closed:** Issue 6 New contact form - OP#18 for Scrum project has been closed by Administrator. ## Configuration @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1) + openproject-gitlab_integration (2.1.1) openproject-webhooks ``` @@ -184,10 +184,10 @@ group :opf_plugins do end ``` -**Note:** It's possible that you need to use these commands before and after the "bundle install" if you get an error in this step warning about a change in the Gemfile: +**Note:** It's possible that you need to use these commands before and after the `bundle install` if you get an error in this step warning about a change in the Gemfile: ```shell -bundle config unset deployment +bundle config unset deployment bundle install --deployment --without mysql2 sqlite development test therubyracer docker bundle config set deployment ``` @@ -211,16 +211,16 @@ In GitLab you have to [set up a webhook](https://docs.gitlab.com/ee/user/project You need to configure just two things in the webhook: 1. The URL must point to your OpenProject server’s GitLab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: - + ``` http://openproject-url.com/webhooks/gitlab?key=ae278268 ``` -2. Enable the required triggers: - 1. Push events - 2. Comments +2. Enable the required triggers: + 1. Push events + 2. Comments 3. Issues events - 4. Merge request events + 4. Merge request events 5. Pipeline events Now the integration is set up on both sides and you can use it. From 54cfe5dd4931d0a496baf3ee3d4466dbb0ae5e81 Mon Sep 17 00:00:00 2001 From: Ben Tey Date: Fri, 1 Dec 2023 21:03:15 -0500 Subject: [PATCH 47/69] Bug - Lack of issues referenced in gitlab tab --- README.md | 4 +- .../notification_handler/note_hook.rb | 16 ++++ .../services/upsert_gitlab_user.rb | 3 +- .../services/upsert_issue.rb | 10 +-- .../services/upsert_issue_note.rb | 88 +++++++++++++++++++ .../services/upsert_merge_request.rb | 10 +-- .../services/upsert_pipeline.rb | 70 +++++++-------- openproject-gitlab_integration.gemspec | 2 +- 8 files changed, 150 insertions(+), 53 deletions(-) create mode 100644 lib/open_project/gitlab_integration/services/upsert_issue_note.rb diff --git a/README.md b/README.md index 0a497bf5b5d4..3c3aeb5010a5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.1 GA +## Introducing OpenProject GitLab Integration v2.1.2 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1.1) + openproject-gitlab_integration (2.1.2) openproject-webhooks ``` diff --git a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb index 0964aee7eec6..b8770ad87d9f 100644 --- a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb @@ -65,6 +65,9 @@ def process(payload_params) notes = generate_notes(payload, 'reference') end comment_on_referenced_work_packages(work_packages, user, notes) + if payload.object_attributes.noteable_type == 'Issue' + upsert_issue(work_packages) + end end private @@ -141,6 +144,19 @@ def generate_notes(payload, note_type) return nil end end + + def gitlab_issue + @gitlab_issue ||= GitlabIssue + .where(gitlab_id: payload.issue.iid) + .or(GitlabIssue.where(gitlab_html_url: payload.issue.url)) + .take + end + + def upsert_issue(work_packages) + return if work_packages.empty? && gitlab_issue.nil? + OpenProject::GitlabIntegration::Services::UpsertIssueNote.new.call(payload, + work_packages: work_packages) + end end end end \ No newline at end of file diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb index fcc9bea55438..23c2531d3166 100644 --- a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb +++ b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2021 the OpenProject GmbH +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -10,6 +10,7 @@ # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: # Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/lib/open_project/gitlab_integration/services/upsert_issue.rb index 969db32a786c..f222ab385d2d 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2021 the OpenProject GmbH +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -10,6 +10,7 @@ # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: # Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -30,12 +31,7 @@ module OpenProject module GitlabIntegration module Services - ## - # Takes merge request data coming from Gitlab webhook data and stores - # them as a `GitlabMergeRequest`. - # If the `GitlabMergeRequest` already exists, it is updated. - # - # Returns the upserted `GitlabMergeRequest`. + class UpsertIssue def call(payload, work_packages: []) find_or_initialize(payload).tap do |issue| diff --git a/lib/open_project/gitlab_integration/services/upsert_issue_note.rb b/lib/open_project/gitlab_integration/services/upsert_issue_note.rb new file mode 100644 index 000000000000..72cb44002534 --- /dev/null +++ b/lib/open_project/gitlab_integration/services/upsert_issue_note.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject + module GitlabIntegration + module Services + + class UpsertIssueNote + def call(payload, work_packages: []) + find_or_initialize(payload).tap do |issue| + issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) + end + end + + private + + def find_or_initialize(payload) + GitlabIssue.find_by_gitlab_identifiers(id: payload.issue.iid, + url: payload.issue.url, + initialize: true) + end + + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + # rubocop:disable Metrics/AbcSize + def extract_params(payload) + { + gitlab_id: payload.issue.iid, + gitlab_user: gitlab_user_id(payload.user), + number: payload.issue.iid, + gitlab_html_url: payload.issue.url, + gitlab_updated_at: payload.issue.updated_at, + state: payload.issue.state, + title: payload.issue.title, + body: description(payload), + repository: payload.repository.name, + labels: payload.issue.labels.map { |values| extract_label_values(values) } + } + end + # rubocop:enable Metrics/AbcSize + + def extract_label_values(payload) + { + title: payload['title'], + color: payload['color'] + } + end + + def gitlab_user_id(payload) + return if payload.blank? + + ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) + end + + def description(payload) + payload.object_attributes.description.presence || 'No description provided' + end + end + end + end +end diff --git a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb index a81176a269b1..cec74aaa6ef4 100644 --- a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb +++ b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb @@ -2,7 +2,7 @@ #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2021 the OpenProject GmbH +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -10,6 +10,7 @@ # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: # Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -30,12 +31,7 @@ module OpenProject module GitlabIntegration module Services - ## - # Takes merge request data coming from Gitlab webhook data and stores - # them as a `GitlabMergeRequest`. - # If the `GitlabMergeRequest` already exists, it is updated. - # - # Returns the upserted `GitlabMergeRequest`. + class UpsertMergeRequest def call(payload, work_packages: []) find_or_initialize(payload).tap do |mr| diff --git a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb index ed91fce2190c..859d74070a58 100644 --- a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb +++ b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb @@ -1,8 +1,8 @@ -#-- encoding: UTF-8 +# frozen_string_literal: true #-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2021 the OpenProject GmbH +# Copyright (C) 2023 Ben Tey # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -10,6 +10,7 @@ # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: # Copyright (C) 2006-2013 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,42 +26,41 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -# See COPYRIGHT and LICENSE files for more details. +# See docs/COPYRIGHT.rdoc for more details. #++ -module OpenProject::GitlabIntegration::Services - ## - # Takes pipelines CI data coming from Gitlab webhook data and stores - # them as a `GitlabPipeline`. - # If the `GitlabPipeline` already exists, it is updated. - # - # Returns the upserted `GitlabPipeline`. - class UpsertPipeline - def call(payload, merge_request:) - GitlabPipeline.find_or_initialize_by(gitlab_id: payload.object_attributes.iid) - .tap do |pipeline| - pipeline.update!(gitlab_merge_request: merge_request, **extract_params(payload)) - end - end +module OpenProject + module GitlabIntegration + module Services + + class UpsertPipeline + def call(payload, merge_request:) + GitlabPipeline.find_or_initialize_by(gitlab_id: payload.object_attributes.iid) + .tap do |pipeline| + pipeline.update!(gitlab_merge_request: merge_request, **extract_params(payload)) + end + end - private + private - # Receives the input from the gitlab webhook and translates them - # to our internal representation. - def extract_params(payload) - { - gitlab_id: payload.object_attributes.iid, - gitlab_html_url: "#{payload.project.web_url}/-/pipelines/#{payload.object_attributes.iid}", - project_id: payload.project.id, - gitlab_user_avatar_url: payload.user.avatar_url, - name: payload.object_attributes.iid, - status: payload.object_attributes.status, - details_url: "#{payload.project.web_url}/-/commit/#{payload.object_attributes.sha[0..7]}", - commit_id: payload.object_attributes.sha[0..7], - username: payload.user.name, - ci_details: payload.builds, - started_at: payload.object_attributes.created_at, - completed_at: payload.object_attributes.finished_at - } + # Receives the input from the gitlab webhook and translates them + # to our internal representation. + def extract_params(payload) + { + gitlab_id: payload.object_attributes.iid, + gitlab_html_url: "#{payload.project.web_url}/-/pipelines/#{payload.object_attributes.iid}", + project_id: payload.project.id, + gitlab_user_avatar_url: payload.user.avatar_url, + name: payload.object_attributes.iid, + status: payload.object_attributes.status, + details_url: "#{payload.project.web_url}/-/commit/#{payload.object_attributes.sha[0..7]}", + commit_id: payload.object_attributes.sha[0..7], + username: payload.user.name, + ci_details: payload.builds, + started_at: payload.object_attributes.created_at, + completed_at: payload.object_attributes.finished_at + } + end + end end end end \ No newline at end of file diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 9c4abf49d3a6..b72ec5075fd7 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| s.name = 'openproject-gitlab_integration' - s.version = '2.1.1' + s.version = '2.1.2' s.authors = 'Ben Tey' s.email = 'ben.tey@outlook.com' s.homepage = 'https://github.com/btey/openproject-gitlab-integration' From a13763905884b8932d15ccae017620d7688d4f3e Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Sun, 3 Dec 2023 03:14:27 +0300 Subject: [PATCH 48/69] services refactoring --- .../services/params_helper.rb | 50 +++++++++++++++++++ .../services/upsert_gitlab_user.rb | 8 ++- .../services/upsert_issue.rb | 7 +-- .../services/upsert_issue_note.rb | 13 ++--- .../services/upsert_merge_request.rb | 9 ++-- .../services/upsert_pipeline.rb | 7 +-- 6 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 lib/open_project/gitlab_integration/services/params_helper.rb diff --git a/lib/open_project/gitlab_integration/services/params_helper.rb b/lib/open_project/gitlab_integration/services/params_helper.rb new file mode 100644 index 000000000000..ef7a9355c03e --- /dev/null +++ b/lib/open_project/gitlab_integration/services/params_helper.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject + module GitlabIntegration + module Services + module ParamsHelper + private + + EMPTY_AVATAR_URL = 'https://www.gravatar.com/avatar/?d=mp' + EMPTY_DESCRIPTION = 'No description provided' + + def avatar_url(raw_url) + raw_url.presence || EMPTY_AVATAR_URL + end + + def description(raw_description) + raw_description.presence || EMPTY_DESCRIPTION + end + end + end + end +end diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb index 23c2531d3166..e9f70e340e4b 100644 --- a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb +++ b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb @@ -38,6 +38,8 @@ module Services # # Returns the upserted `GitlabUser`. class UpsertGitlabUser + include ParamsHelper + def call(payload) GitlabUser.find_or_initialize_by(gitlab_id: payload.id) .tap do |gitlab_user| @@ -56,13 +58,9 @@ def extract_params(payload) gitlab_name: payload.name, gitlab_username: payload.username, gitlab_email: payload.email, - gitlab_avatar_url: avatar_url(payload) + gitlab_avatar_url: avatar_url(payload.avatar_url) } end - - def avatar_url(payload) - payload.avatar_url.presence || 'https://www.gravatar.com/avatar/?d=mp' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/lib/open_project/gitlab_integration/services/upsert_issue.rb index f222ab385d2d..b0e50f8f1d32 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue.rb @@ -31,7 +31,6 @@ module OpenProject module GitlabIntegration module Services - class UpsertIssue def call(payload, work_packages: []) find_or_initialize(payload).tap do |issue| @@ -59,7 +58,7 @@ def extract_params(payload) gitlab_updated_at: payload.object_attributes.updated_at, state: payload.object_attributes.state, title: payload.object_attributes.title, - body: description(payload), + body: description(payload.object_attributes.description), repository: payload.repository.name, labels: payload.labels.map { |values| extract_label_values(values) } } @@ -78,10 +77,6 @@ def gitlab_user_id(payload) ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) end - - def description(payload) - payload.object_attributes.description.presence || 'No description provided' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue_note.rb b/lib/open_project/gitlab_integration/services/upsert_issue_note.rb index 72cb44002534..eb58b8e83e33 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue_note.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue_note.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertIssueNote + include ParamsHelper + def call(payload, work_packages: []) find_or_initialize(payload).tap do |issue| issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) @@ -43,8 +44,8 @@ def call(payload, work_packages: []) def find_or_initialize(payload) GitlabIssue.find_by_gitlab_identifiers(id: payload.issue.iid, - url: payload.issue.url, - initialize: true) + url: payload.issue.url, + initialize: true) end # Receives the input from the gitlab webhook and translates them @@ -59,7 +60,7 @@ def extract_params(payload) gitlab_updated_at: payload.issue.updated_at, state: payload.issue.state, title: payload.issue.title, - body: description(payload), + body: description(payload.object_attributes.description), repository: payload.repository.name, labels: payload.issue.labels.map { |values| extract_label_values(values) } } @@ -78,10 +79,6 @@ def gitlab_user_id(payload) ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) end - - def description(payload) - payload.object_attributes.description.presence || 'No description provided' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb index cec74aaa6ef4..78c8c0ccfa00 100644 --- a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb +++ b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertMergeRequest + include ParamsHelper + def call(payload, work_packages: []) find_or_initialize(payload).tap do |mr| mr.update!(work_packages: mr.work_packages | work_packages, **extract_params(payload)) @@ -59,7 +60,7 @@ def extract_params(payload) gitlab_updated_at: payload.object_attributes.updated_at, state: payload.object_attributes.state, title: payload.object_attributes.title, - body: description(payload), + body: description(payload.object_attributes.description), repository: payload.repository.name, draft: payload.object_attributes.work_in_progress, merged: payload.object_attributes.state == 'merged', @@ -82,10 +83,6 @@ def gitlab_user_id(payload) ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) end - - def description(payload) - payload.object_attributes.description.presence || 'No description provided' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb index 859d74070a58..0fbdd9ecf7ec 100644 --- a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb +++ b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertPipeline + include ParamsHelper + def call(payload, merge_request:) GitlabPipeline.find_or_initialize_by(gitlab_id: payload.object_attributes.iid) .tap do |pipeline| @@ -49,7 +50,7 @@ def extract_params(payload) gitlab_id: payload.object_attributes.iid, gitlab_html_url: "#{payload.project.web_url}/-/pipelines/#{payload.object_attributes.iid}", project_id: payload.project.id, - gitlab_user_avatar_url: payload.user.avatar_url, + gitlab_user_avatar_url: avatar_url(payload.user.avatar_url), name: payload.object_attributes.iid, status: payload.object_attributes.status, details_url: "#{payload.project.web_url}/-/commit/#{payload.object_attributes.sha[0..7]}", @@ -63,4 +64,4 @@ def extract_params(payload) end end end -end \ No newline at end of file +end From 700788cab092c06fb31a230df61a8f11aee46040 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Mon, 4 Dec 2023 14:01:53 +0300 Subject: [PATCH 49/69] gem openproject-gitlab_integration 2.1.3 updated --- README.md | 4 ++-- openproject-gitlab_integration.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3c3aeb5010a5..8d0e4f1072fe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.2 GA +## Introducing OpenProject GitLab Integration v2.1.3 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1.2) + openproject-gitlab_integration (2.1.3) openproject-webhooks ``` diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index b72ec5075fd7..9510dabadca4 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| s.name = 'openproject-gitlab_integration' - s.version = '2.1.2' + s.version = '2.1.3' s.authors = 'Ben Tey' s.email = 'ben.tey@outlook.com' s.homepage = 'https://github.com/btey/openproject-gitlab-integration' From 3f8b75e0163fcd7e3e52aa7b7fab69fd054074e0 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Mon, 4 Dec 2023 18:45:20 +0300 Subject: [PATCH 50/69] module Services fix --- lib/open_project/gitlab_integration/services.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/open_project/gitlab_integration/services.rb b/lib/open_project/gitlab_integration/services.rb index adce10305094..288dc4281545 100644 --- a/lib/open_project/gitlab_integration/services.rb +++ b/lib/open_project/gitlab_integration/services.rb @@ -27,6 +27,7 @@ # See docs/COPYRIGHT.rdoc for more details. #++ +require_relative './services/params_helper' require_relative './services/upsert_pipeline' require_relative './services/upsert_gitlab_user' require_relative './services/upsert_merge_request' From 3e71033859b5b3bb4d58bc54f7959563e209991b Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Mon, 4 Dec 2023 19:34:41 +0300 Subject: [PATCH 51/69] fix --- .../notification_handler/note_hook.rb | 123 +++++++++--------- .../services/upsert_issue.rb | 2 + 2 files changed, 65 insertions(+), 60 deletions(-) diff --git a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb index b8770ad87d9f..3bffd2683413 100644 --- a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb @@ -33,33 +33,36 @@ module NotificationHandler # Handles Gitlab comment notifications. class NoteHook include OpenProject::GitlabIntegration::NotificationHandler::Helper - + # TODO: this can be more refactored and simplified... def process(payload_params) @payload = wrap_payload(payload_params) - user = User.find_by_id(payload.open_project_user_id) + user = User.find_by(id: payload.open_project_user_id) text = payload.object_attributes.note work_packages = find_mentioned_work_packages(text, user, payload.object_kind) if work_packages.empty? && payload.object_attributes.noteable_type == 'Issue' - text = payload.issue.title + ' - ' + payload.object_attributes.note + text = "#{payload.issue.title} - #{payload.object_attributes.note}" work_packages = find_mentioned_work_packages(text, user, payload.object_kind) work_packages_excluded = find_excluded_work_packages(text, user) work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? return if work_packages.empty? + notes = generate_notes(payload, 'comment') elsif work_packages.empty? && payload.object_attributes.noteable_type == 'Snippet' - text = payload.snippet.title + ' - ' + payload.object_attributes.note + text = "#{payload.snippet.title} - #{payload.object_attributes.note}" work_packages = find_mentioned_work_packages(text, user, payload.object_kind) work_packages_excluded = find_excluded_work_packages(text, user) work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? return if work_packages.empty? + notes = generate_notes(payload, 'reference') elsif work_packages.empty? && payload.object_attributes.noteable_type == 'MergeRequest' - text = payload.merge_request.title + ' - ' + payload.object_attributes.note + text = "#{payload.merge_request.title} - #{payload.object_attributes.note}" work_packages = find_mentioned_work_packages(text, user, payload.object_kind) work_packages_excluded = find_excluded_work_packages(text, user) work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? return if work_packages.empty? + notes = generate_notes(payload, 'comment') else notes = generate_notes(payload, 'reference') @@ -76,72 +79,71 @@ def process(payload_params) # TODO: add key list to simplify the code... def generate_notes(payload, note_type) - if payload.object_attributes.noteable_type == 'Commit' + case payload.object_attributes.noteable_type + when 'Commit' commit_id = payload.commit.id I18n.t("gitlab_integration.note_commit_referenced_comment", - :commit_id => commit_id[0, 8], - :commit_url => payload.object_attributes.url, - :commit_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) - elsif payload.object_attributes.noteable_type == 'MergeRequest' + commit_id: commit_id[0, 8], + commit_url: payload.object_attributes.url, + commit_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) + when 'MergeRequest' if note_type == 'comment' I18n.t("gitlab_integration.note_mr_commented_comment", - :mr_number => payload.merge_request.iid, - :mr_title => payload.merge_request.title, - :mr_url => payload.object_attributes.url, - :mr_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + mr_number: payload.merge_request.iid, + mr_title: payload.merge_request.title, + mr_url: payload.object_attributes.url, + mr_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) elsif note_type == 'reference' I18n.t("gitlab_integration.note_mr_referenced_comment", - :mr_number => payload.merge_request.iid, - :mr_title => payload.merge_request.title, - :mr_url => payload.object_attributes.url, - :mr_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + mr_number: payload.merge_request.iid, + mr_title: payload.merge_request.title, + mr_url: payload.object_attributes.url, + mr_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) end - elsif payload.object_attributes.noteable_type == 'Issue' + when 'Issue' if note_type == 'comment' I18n.t("gitlab_integration.note_issue_commented_comment", - :issue_number => payload.issue.iid, - :issue_title => payload.issue.title, - :issue_url => payload.object_attributes.url, - :issue_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + issue_number: payload.issue.iid, + issue_title: payload.issue.title, + issue_url: payload.object_attributes.url, + issue_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) elsif note_type == 'reference' I18n.t("gitlab_integration.note_issue_referenced_comment", - :issue_number => payload.issue.iid, - :issue_title => payload.issue.title, - :issue_url => payload.object_attributes.url, - :issue_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + issue_number: payload.issue.iid, + issue_title: payload.issue.title, + issue_url: payload.object_attributes.url, + issue_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) end - elsif payload.object_attributes.noteable_type == 'Snippet' + when 'Snippet' I18n.t("gitlab_integration.note_snippet_referenced_comment", - :snippet_number => payload.snippet.id, - :snippet_title => payload.snippet.title, - :snippet_url => payload.object_attributes.url, - :snippet_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) - else - return nil + snippet_number: payload.snippet.id, + snippet_title: payload.snippet.title, + snippet_url: payload.object_attributes.url, + snippet_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) end end @@ -154,9 +156,10 @@ def gitlab_issue def upsert_issue(work_packages) return if work_packages.empty? && gitlab_issue.nil? + OpenProject::GitlabIntegration::Services::UpsertIssueNote.new.call(payload, - work_packages: work_packages) + work_packages:) end end end -end \ No newline at end of file +end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/lib/open_project/gitlab_integration/services/upsert_issue.rb index b0e50f8f1d32..6eb2298d5f9c 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue.rb @@ -32,6 +32,8 @@ module OpenProject module GitlabIntegration module Services class UpsertIssue + include ParamsHelper + def call(payload, work_packages: []) find_or_initialize(payload).tap do |issue| issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) From d8cb06d9fdd0840bddab703dc90c875521edf6fe Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Tue, 5 Dec 2023 08:06:35 +0300 Subject: [PATCH 52/69] prevent 500 errors: undefined method `iid?` for nil:NilClass when pipeline_hook requested without merge_request --- .../notification_handler/pipeline_hook.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb index 1c14b01fb171..c591e18957a3 100644 --- a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb @@ -37,14 +37,16 @@ class PipelineHook def process(payload_params) @payload = wrap_payload(payload_params) - return unless associated_with_mr? + return if payload.merge_request.blank? + merge_request = find_merge_request return unless merge_request + return unless associated_with_mr? # disabled until gitlab issue resolution OpenProject::GitlabIntegration::Services::UpsertPipeline.new.call( payload, - merge_request: merge_request + merge_request: ) end @@ -53,12 +55,12 @@ def process(payload_params) attr_reader :payload def associated_with_mr? - payload.merge_request.iid?.present? + payload.merge_request.iid.present? end def find_merge_request gitlab_id = payload.merge_request.iid - GitlabMergeRequest.find_by(gitlab_id: gitlab_id) + GitlabMergeRequest.find_by(gitlab_id:) end end end From 782a06d1344063dd130f7c8f796b5ccd59f6e280 Mon Sep 17 00:00:00 2001 From: Benjamin Tey Date: Wed, 13 Dec 2023 21:22:22 -0500 Subject: [PATCH 53/69] Initial hotfix for OP v13.1.0 - Permission method requires permissible_on param --- lib/open_project/gitlab_integration/engine.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 4daf619f8fc9..2ec4e2a1b979 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -44,7 +44,10 @@ class Engine < ::Rails::Engine :author_url => 'https://github.com/btey/openproject', bundled: true do project_module(:gitlab, dependencies: :work_package_tracking) do - permission(:show_gitlab_content, {}) + # permission(:show_gitlab_content, {}) + permission(:show_gitlab_content, + {}, + permissible_on: %i[work_package project]) end end From 557f302b8ab9dc8cb988a243c8ef39e8295ed4c9 Mon Sep 17 00:00:00 2001 From: "Tey, Benjamin" Date: Thu, 14 Dec 2023 08:46:28 -0500 Subject: [PATCH 54/69] Remove old permission call and update readme. --- README.md | 6 +++--- lib/open_project/gitlab_integration/engine.rb | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3c3aeb5010a5..f204837fff21 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.2 GA +## Introducing OpenProject GitLab Integration v2.1.3 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -14,8 +14,8 @@ If there are labels related to the Issue or MR, a button with the label icon wil ## Overview OpenProject module for integration with GitLab: -* Latest Gitlab release tested: **16.5.1** -* Latest OpenProject release tested: **13.0.8** +* Latest Gitlab release tested: **16.6.2** +* Latest OpenProject release tested: **13.1.0** (for OP v13.0.X use v2.1.2) The reference system is based on the same system as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title/description of the Issue/MR in GitLab. diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 2ec4e2a1b979..752d2cb42c0a 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -44,7 +44,6 @@ class Engine < ::Rails::Engine :author_url => 'https://github.com/btey/openproject', bundled: true do project_module(:gitlab, dependencies: :work_package_tracking) do - # permission(:show_gitlab_content, {}) permission(:show_gitlab_content, {}, permissible_on: %i[work_package project]) From a0b293db2ffd21bb4e24f731e84cdb42366b3db0 Mon Sep 17 00:00:00 2001 From: btey Date: Thu, 14 Dec 2023 08:55:18 -0500 Subject: [PATCH 55/69] Fix version in gemspecs file --- openproject-gitlab_integration.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index b72ec5075fd7..9510dabadca4 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| s.name = 'openproject-gitlab_integration' - s.version = '2.1.2' + s.version = '2.1.3' s.authors = 'Ben Tey' s.email = 'ben.tey@outlook.com' s.homepage = 'https://github.com/btey/openproject-gitlab-integration' From d97c0f82ed3a53fa152614b0b3fe3a6b02e366c3 Mon Sep 17 00:00:00 2001 From: Benjamin Tey Date: Sun, 17 Dec 2023 16:38:59 -0500 Subject: [PATCH 56/69] More fixes for OP v13.1.0 --- README.md | 4 ++-- lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb | 2 +- .../gitlab_merge_requests_by_work_package_api.rb | 2 +- .../gitlab_integration/notification_handler/helper.rb | 2 +- .../patches/api/work_package_representer.rb | 2 +- openproject-gitlab_integration.gemspec | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f204837fff21..a39be3e20d45 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.3 GA +## Introducing OpenProject GitLab Integration v2.1.4 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1.2) + openproject-gitlab_integration (2.1.4) openproject-webhooks ``` diff --git a/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb b/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb index b715058e4242..ab59f2b1c94f 100644 --- a/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb +++ b/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb @@ -34,7 +34,7 @@ module V3 module GitlabIssues class GitlabIssuesByWorkPackageAPI < ::API::OpenProjectAPI after_validation do - authorize(:show_gitlab_content, context: @work_package.project) + authorize_in_work_package(:show_gitlab_content, work_package: @work_package) @gitlab_issues = @work_package.gitlab_issues end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb index 3976818491b8..a2de4eacdbeb 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb @@ -34,7 +34,7 @@ module V3 module GitlabMergeRequests class GitlabMergeRequestsByWorkPackageAPI < ::API::OpenProjectAPI after_validation do - authorize(:show_gitlab_content, context: @work_package.project) + authorize_in_work_package(:show_gitlab_content, work_package: @work_package) @gitlab_merge_requests = @work_package.gitlab_merge_requests end diff --git a/lib/open_project/gitlab_integration/notification_handler/helper.rb b/lib/open_project/gitlab_integration/notification_handler/helper.rb index 396ff1e35679..7f9a0eed5032 100644 --- a/lib/open_project/gitlab_integration/notification_handler/helper.rb +++ b/lib/open_project/gitlab_integration/notification_handler/helper.rb @@ -75,7 +75,7 @@ def find_visible_work_packages(ids, user) WorkPackage .includes(:project) .where(id: ids) - .select { |wp| user.allowed_to?(:add_work_package_notes, wp.project) } + .select { |wp| user.allowed_in_work_package?(:add_work_package_notes, wp) } end # Returns a list of `WorkPackage`s that were referenced in the `text` and are visible to the given `user`. diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb index 0c2a8cf1c095..e16f17e6a1f7 100644 --- a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb +++ b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb @@ -35,7 +35,7 @@ module WorkPackageRepresenter def extension ->(*) do link :gitlab, - cache_if: -> { current_user.allowed_to?(:show_gitlab_content, represented.project) } do + cache_if: -> { current_user.allowed_in_work_package?(:show_gitlab_content, represented) } do next if represented.new_record? { diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 9510dabadca4..d5fe6edce691 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| s.name = 'openproject-gitlab_integration' - s.version = '2.1.3' + s.version = '2.1.4' s.authors = 'Ben Tey' s.email = 'ben.tey@outlook.com' s.homepage = 'https://github.com/btey/openproject-gitlab-integration' From f38ef6fb8549b3c475f57b7ecc2d28180f16a9c4 Mon Sep 17 00:00:00 2001 From: Aleksey Kurepin Date: Thu, 21 Dec 2023 01:47:18 +0300 Subject: [PATCH 57/69] refactoring --- README.md | 4 +- config/locales/crowdin/js-ru.yml | 8 +- config/locales/crowdin/ru.yml | 2 +- .../gitlab_issues_by_work_package_api.rb | 6 +- ...tlab_merge_requests_by_work_package_api.rb | 2 +- lib/open_project/gitlab_integration/engine.rb | 2 +- .../notification_handler/helper.rb | 23 ++-- .../notification_handler/note_hook.rb | 123 +++++++++--------- .../notification_handler/pipeline_hook.rb | 10 +- .../patches/api/work_package_representer.rb | 2 +- .../gitlab_integration/services.rb | 1 + .../services/params_helper.rb | 50 +++++++ .../services/upsert_gitlab_user.rb | 8 +- .../services/upsert_issue.rb | 9 +- .../services/upsert_issue_note.rb | 13 +- .../services/upsert_merge_request.rb | 9 +- .../services/upsert_pipeline.rb | 7 +- openproject-gitlab_integration.gemspec | 2 +- 18 files changed, 163 insertions(+), 118 deletions(-) create mode 100644 lib/open_project/gitlab_integration/services/params_helper.rb diff --git a/README.md b/README.md index a39be3e20d45..3ccbc0e9ccd4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.4 GA +## Introducing OpenProject GitLab Integration v2.1.5 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1.4) + openproject-gitlab_integration (2.1.5) openproject-webhooks ``` diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index 9e612d36e9ef..e1877b525340 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -39,18 +39,18 @@ ru: label: Создать запрос на слияние description: Создать запрос на слияние copy_menu: - label: Git-сниппеты - description: Скопипастить Git-сниппеты + label: Git-подсказки + description: Скопипастить Git-подсказки git_actions: branch_name: Имя ветки commit_message: Описание коммита cmd: Создать ветку с пустым коммитом - title: Быстрые сниппеты для Git + title: Git-подсказки copy_success: ✅ Скопировано copy_error: ❌ Не получилось скопировать tab_issue: empty: Нет связанных обсуждений. Привязать обсуждение можно путём вставки кода OP#%{wp_id} (или PP#%{wp_id} для приватных ссылок) в заголовке/описании при создании/редактировании обсуждения. tab_mrs: - empty: Нет связанных запросов на слияние. Привязать запрос на слияние можно путём вставки кода OP#%{wp_id} (или PP#%{wp_id} для приватных ссылок) в заголовке/описании при создании/редактировании запроса на слияние. ОБЯЗАТЕЛЬНО заполните описание! + empty: Нет связанных запросов на слияние. Привязать запрос на слияние можно путём вставки кода OP#%{wp_id} (или PP#%{wp_id} для приватных ссылок) в заголовке/описании при создании/редактировании запроса на слияние. gitlab_pipelines: Сборочные линии (pipelines) updated_on: Обновлено diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 3da85dbd8109..829c50e3fcbd 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -82,7 +82,7 @@ ru: **Обсуждение переоткрыто:** Обсуждение %{issue_number} [%{issue_title}](%{issue_url}) в [%{repository}](%{repository_url}) было переоткрыто [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > - **Коммит в MR:** [%{gitlab_user}](%{gitlab_user_url}) закоммитил [%{commit_number}](%{commit_url}) + **Коммит:** [%{gitlab_user}](%{gitlab_user_url}) закоммитил [%{commit_number}](%{commit_url}) в [%{repository}](%{repository_url}) %{commit_timestamp}: %{commit_note} diff --git a/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb b/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb index ab59f2b1c94f..7604173b4060 100644 --- a/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb +++ b/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb @@ -42,9 +42,9 @@ class GitlabIssuesByWorkPackageAPI < ::API::OpenProjectAPI get do path = api_v3_paths.gitlab_issues_by_work_package(@work_package.id) GitlabIssueCollectionRepresenter.new(@gitlab_issues, - @gitlab_issues.count, - self_link: path, - current_user: current_user) + @gitlab_issues.count, + self_link: path, + current_user:) end end end diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb index a2de4eacdbeb..8627ba320710 100644 --- a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb +++ b/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb @@ -44,7 +44,7 @@ class GitlabMergeRequestsByWorkPackageAPI < ::API::OpenProjectAPI GitlabMergeRequestCollectionRepresenter.new(@gitlab_merge_requests, @gitlab_merge_requests.count, self_link: path, - current_user: current_user) + current_user:) end end end diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 752d2cb42c0a..ce1cc92baf85 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -93,7 +93,7 @@ class Engine < ::Rails::Engine add_api_endpoint 'API::V3::WorkPackages::WorkPackagesAPI', :id do mount ::API::V3::GitlabMergeRequests::GitlabMergeRequestsByWorkPackageAPI end - + add_api_endpoint 'API::V3::WorkPackages::WorkPackagesAPI', :id do mount ::API::V3::GitlabIssues::GitlabIssuesByWorkPackageAPI end diff --git a/lib/open_project/gitlab_integration/notification_handler/helper.rb b/lib/open_project/gitlab_integration/notification_handler/helper.rb index 7f9a0eed5032..cf715d0bb69c 100644 --- a/lib/open_project/gitlab_integration/notification_handler/helper.rb +++ b/lib/open_project/gitlab_integration/notification_handler/helper.rb @@ -30,7 +30,6 @@ module OpenProject::GitlabIntegration module NotificationHandler module Helper - ## # Parses the given source string and returns a list of work_package ids # which it finds and should be considered public or private. @@ -49,13 +48,13 @@ def extract_work_package_ids(text, kind = "") # e.g.,: This is a reference to OP#1234 # For private comments you can use the prefix: PP# host_name = Regexp.escape(Setting.host_name) - if kind == 'private' - wp_regex = /PP#(\d+)/ - elsif kind != 'note' - wp_regex = /OP#(\d+)|PP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ - else - wp_regex = /OP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ - end + wp_regex = if kind == 'private' + /PP#(\d+)/ + elsif kind != 'note' + /OP#(\d+)|PP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ + else + /OP#(\d+)|http(?:s?):\/\/#{host_name}\/(?:\S+?\/)*(?:work_packages|wp)\/([0-9]+)/ + end String(text) .scan(wp_regex) .map { |first, second| (first || second).to_i } @@ -92,9 +91,10 @@ def find_excluded_work_packages(text, user) # Adds comments to the given WorkPackages. def comment_on_referenced_work_packages(work_packages, user, notes) return if notes.nil? + work_packages.each do |work_package| ::WorkPackages::UpdateService - .new(user: user, model: work_package) + .new(user:, model: work_package) .call(journal_notes: notes, send_notifications: false) end end @@ -104,7 +104,7 @@ def comment_on_referenced_work_packages(work_packages, user, notes) def status_on_referenced_work_packages(work_packages, user, status) work_packages.each do |work_package| ::WorkPackages::UpdateService - .new(user: user, model: work_package) + .new(user:, model: work_package) .call(status_id: status) end end @@ -145,7 +145,6 @@ def respond_to_missing?(_method_name, _include_private = false) def wrap_payload(payload) Payload.new(payload) end - end end -end \ No newline at end of file +end diff --git a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb index b8770ad87d9f..3bffd2683413 100644 --- a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/note_hook.rb @@ -33,33 +33,36 @@ module NotificationHandler # Handles Gitlab comment notifications. class NoteHook include OpenProject::GitlabIntegration::NotificationHandler::Helper - + # TODO: this can be more refactored and simplified... def process(payload_params) @payload = wrap_payload(payload_params) - user = User.find_by_id(payload.open_project_user_id) + user = User.find_by(id: payload.open_project_user_id) text = payload.object_attributes.note work_packages = find_mentioned_work_packages(text, user, payload.object_kind) if work_packages.empty? && payload.object_attributes.noteable_type == 'Issue' - text = payload.issue.title + ' - ' + payload.object_attributes.note + text = "#{payload.issue.title} - #{payload.object_attributes.note}" work_packages = find_mentioned_work_packages(text, user, payload.object_kind) work_packages_excluded = find_excluded_work_packages(text, user) work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? return if work_packages.empty? + notes = generate_notes(payload, 'comment') elsif work_packages.empty? && payload.object_attributes.noteable_type == 'Snippet' - text = payload.snippet.title + ' - ' + payload.object_attributes.note + text = "#{payload.snippet.title} - #{payload.object_attributes.note}" work_packages = find_mentioned_work_packages(text, user, payload.object_kind) work_packages_excluded = find_excluded_work_packages(text, user) work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? return if work_packages.empty? + notes = generate_notes(payload, 'reference') elsif work_packages.empty? && payload.object_attributes.noteable_type == 'MergeRequest' - text = payload.merge_request.title + ' - ' + payload.object_attributes.note + text = "#{payload.merge_request.title} - #{payload.object_attributes.note}" work_packages = find_mentioned_work_packages(text, user, payload.object_kind) work_packages_excluded = find_excluded_work_packages(text, user) work_packages = work_packages - work_packages_excluded unless work_packages_excluded.empty? return if work_packages.empty? + notes = generate_notes(payload, 'comment') else notes = generate_notes(payload, 'reference') @@ -76,72 +79,71 @@ def process(payload_params) # TODO: add key list to simplify the code... def generate_notes(payload, note_type) - if payload.object_attributes.noteable_type == 'Commit' + case payload.object_attributes.noteable_type + when 'Commit' commit_id = payload.commit.id I18n.t("gitlab_integration.note_commit_referenced_comment", - :commit_id => commit_id[0, 8], - :commit_url => payload.object_attributes.url, - :commit_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) - elsif payload.object_attributes.noteable_type == 'MergeRequest' + commit_id: commit_id[0, 8], + commit_url: payload.object_attributes.url, + commit_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) + when 'MergeRequest' if note_type == 'comment' I18n.t("gitlab_integration.note_mr_commented_comment", - :mr_number => payload.merge_request.iid, - :mr_title => payload.merge_request.title, - :mr_url => payload.object_attributes.url, - :mr_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + mr_number: payload.merge_request.iid, + mr_title: payload.merge_request.title, + mr_url: payload.object_attributes.url, + mr_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) elsif note_type == 'reference' I18n.t("gitlab_integration.note_mr_referenced_comment", - :mr_number => payload.merge_request.iid, - :mr_title => payload.merge_request.title, - :mr_url => payload.object_attributes.url, - :mr_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + mr_number: payload.merge_request.iid, + mr_title: payload.merge_request.title, + mr_url: payload.object_attributes.url, + mr_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) end - elsif payload.object_attributes.noteable_type == 'Issue' + when 'Issue' if note_type == 'comment' I18n.t("gitlab_integration.note_issue_commented_comment", - :issue_number => payload.issue.iid, - :issue_title => payload.issue.title, - :issue_url => payload.object_attributes.url, - :issue_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + issue_number: payload.issue.iid, + issue_title: payload.issue.title, + issue_url: payload.object_attributes.url, + issue_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) elsif note_type == 'reference' I18n.t("gitlab_integration.note_issue_referenced_comment", - :issue_number => payload.issue.iid, - :issue_title => payload.issue.title, - :issue_url => payload.object_attributes.url, - :issue_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) + issue_number: payload.issue.iid, + issue_title: payload.issue.title, + issue_url: payload.object_attributes.url, + issue_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) end - elsif payload.object_attributes.noteable_type == 'Snippet' + when 'Snippet' I18n.t("gitlab_integration.note_snippet_referenced_comment", - :snippet_number => payload.snippet.id, - :snippet_title => payload.snippet.title, - :snippet_url => payload.object_attributes.url, - :snippet_note => payload.object_attributes.note, - :repository => payload.repository.name, - :repository_url => payload.repository.homepage, - :gitlab_user => payload.user.name, - :gitlab_user_url => payload.user.avatar_url) - else - return nil + snippet_number: payload.snippet.id, + snippet_title: payload.snippet.title, + snippet_url: payload.object_attributes.url, + snippet_note: payload.object_attributes.note, + repository: payload.repository.name, + repository_url: payload.repository.homepage, + gitlab_user: payload.user.name, + gitlab_user_url: payload.user.avatar_url) end end @@ -154,9 +156,10 @@ def gitlab_issue def upsert_issue(work_packages) return if work_packages.empty? && gitlab_issue.nil? + OpenProject::GitlabIntegration::Services::UpsertIssueNote.new.call(payload, - work_packages: work_packages) + work_packages:) end end end -end \ No newline at end of file +end diff --git a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb index 1c14b01fb171..c591e18957a3 100644 --- a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb @@ -37,14 +37,16 @@ class PipelineHook def process(payload_params) @payload = wrap_payload(payload_params) - return unless associated_with_mr? + return if payload.merge_request.blank? + merge_request = find_merge_request return unless merge_request + return unless associated_with_mr? # disabled until gitlab issue resolution OpenProject::GitlabIntegration::Services::UpsertPipeline.new.call( payload, - merge_request: merge_request + merge_request: ) end @@ -53,12 +55,12 @@ def process(payload_params) attr_reader :payload def associated_with_mr? - payload.merge_request.iid?.present? + payload.merge_request.iid.present? end def find_merge_request gitlab_id = payload.merge_request.iid - GitlabMergeRequest.find_by(gitlab_id: gitlab_id) + GitlabMergeRequest.find_by(gitlab_id:) end end end diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb index e16f17e6a1f7..a83a247e96fe 100644 --- a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb +++ b/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb @@ -32,7 +32,7 @@ module API module WorkPackageRepresenter module_function - def extension + def extension # rubocop:disable Metrics/AbcSize ->(*) do link :gitlab, cache_if: -> { current_user.allowed_in_work_package?(:show_gitlab_content, represented) } do diff --git a/lib/open_project/gitlab_integration/services.rb b/lib/open_project/gitlab_integration/services.rb index adce10305094..288dc4281545 100644 --- a/lib/open_project/gitlab_integration/services.rb +++ b/lib/open_project/gitlab_integration/services.rb @@ -27,6 +27,7 @@ # See docs/COPYRIGHT.rdoc for more details. #++ +require_relative './services/params_helper' require_relative './services/upsert_pipeline' require_relative './services/upsert_gitlab_user' require_relative './services/upsert_merge_request' diff --git a/lib/open_project/gitlab_integration/services/params_helper.rb b/lib/open_project/gitlab_integration/services/params_helper.rb new file mode 100644 index 000000000000..ef7a9355c03e --- /dev/null +++ b/lib/open_project/gitlab_integration/services/params_helper.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ +module OpenProject + module GitlabIntegration + module Services + module ParamsHelper + private + + EMPTY_AVATAR_URL = 'https://www.gravatar.com/avatar/?d=mp' + EMPTY_DESCRIPTION = 'No description provided' + + def avatar_url(raw_url) + raw_url.presence || EMPTY_AVATAR_URL + end + + def description(raw_description) + raw_description.presence || EMPTY_DESCRIPTION + end + end + end + end +end diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb index 23c2531d3166..e9f70e340e4b 100644 --- a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb +++ b/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb @@ -38,6 +38,8 @@ module Services # # Returns the upserted `GitlabUser`. class UpsertGitlabUser + include ParamsHelper + def call(payload) GitlabUser.find_or_initialize_by(gitlab_id: payload.id) .tap do |gitlab_user| @@ -56,13 +58,9 @@ def extract_params(payload) gitlab_name: payload.name, gitlab_username: payload.username, gitlab_email: payload.email, - gitlab_avatar_url: avatar_url(payload) + gitlab_avatar_url: avatar_url(payload.avatar_url) } end - - def avatar_url(payload) - payload.avatar_url.presence || 'https://www.gravatar.com/avatar/?d=mp' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/lib/open_project/gitlab_integration/services/upsert_issue.rb index f222ab385d2d..6eb2298d5f9c 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertIssue + include ParamsHelper + def call(payload, work_packages: []) find_or_initialize(payload).tap do |issue| issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) @@ -59,7 +60,7 @@ def extract_params(payload) gitlab_updated_at: payload.object_attributes.updated_at, state: payload.object_attributes.state, title: payload.object_attributes.title, - body: description(payload), + body: description(payload.object_attributes.description), repository: payload.repository.name, labels: payload.labels.map { |values| extract_label_values(values) } } @@ -78,10 +79,6 @@ def gitlab_user_id(payload) ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) end - - def description(payload) - payload.object_attributes.description.presence || 'No description provided' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_issue_note.rb b/lib/open_project/gitlab_integration/services/upsert_issue_note.rb index 72cb44002534..eb58b8e83e33 100644 --- a/lib/open_project/gitlab_integration/services/upsert_issue_note.rb +++ b/lib/open_project/gitlab_integration/services/upsert_issue_note.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertIssueNote + include ParamsHelper + def call(payload, work_packages: []) find_or_initialize(payload).tap do |issue| issue.update!(work_packages: issue.work_packages | work_packages, **extract_params(payload)) @@ -43,8 +44,8 @@ def call(payload, work_packages: []) def find_or_initialize(payload) GitlabIssue.find_by_gitlab_identifiers(id: payload.issue.iid, - url: payload.issue.url, - initialize: true) + url: payload.issue.url, + initialize: true) end # Receives the input from the gitlab webhook and translates them @@ -59,7 +60,7 @@ def extract_params(payload) gitlab_updated_at: payload.issue.updated_at, state: payload.issue.state, title: payload.issue.title, - body: description(payload), + body: description(payload.object_attributes.description), repository: payload.repository.name, labels: payload.issue.labels.map { |values| extract_label_values(values) } } @@ -78,10 +79,6 @@ def gitlab_user_id(payload) ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) end - - def description(payload) - payload.object_attributes.description.presence || 'No description provided' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb index cec74aaa6ef4..78c8c0ccfa00 100644 --- a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb +++ b/lib/open_project/gitlab_integration/services/upsert_merge_request.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertMergeRequest + include ParamsHelper + def call(payload, work_packages: []) find_or_initialize(payload).tap do |mr| mr.update!(work_packages: mr.work_packages | work_packages, **extract_params(payload)) @@ -59,7 +60,7 @@ def extract_params(payload) gitlab_updated_at: payload.object_attributes.updated_at, state: payload.object_attributes.state, title: payload.object_attributes.title, - body: description(payload), + body: description(payload.object_attributes.description), repository: payload.repository.name, draft: payload.object_attributes.work_in_progress, merged: payload.object_attributes.state == 'merged', @@ -82,10 +83,6 @@ def gitlab_user_id(payload) ::OpenProject::GitlabIntegration::Services::UpsertGitlabUser.new.call(payload) end - - def description(payload) - payload.object_attributes.description.presence || 'No description provided' - end end end end diff --git a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb index 859d74070a58..0fbdd9ecf7ec 100644 --- a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb +++ b/lib/open_project/gitlab_integration/services/upsert_pipeline.rb @@ -31,8 +31,9 @@ module OpenProject module GitlabIntegration module Services - class UpsertPipeline + include ParamsHelper + def call(payload, merge_request:) GitlabPipeline.find_or_initialize_by(gitlab_id: payload.object_attributes.iid) .tap do |pipeline| @@ -49,7 +50,7 @@ def extract_params(payload) gitlab_id: payload.object_attributes.iid, gitlab_html_url: "#{payload.project.web_url}/-/pipelines/#{payload.object_attributes.iid}", project_id: payload.project.id, - gitlab_user_avatar_url: payload.user.avatar_url, + gitlab_user_avatar_url: avatar_url(payload.user.avatar_url), name: payload.object_attributes.iid, status: payload.object_attributes.status, details_url: "#{payload.project.web_url}/-/commit/#{payload.object_attributes.sha[0..7]}", @@ -63,4 +64,4 @@ def extract_params(payload) end end end -end \ No newline at end of file +end diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index d5fe6edce691..8acf7073c24a 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| s.name = 'openproject-gitlab_integration' - s.version = '2.1.4' + s.version = '2.1.5' s.authors = 'Ben Tey' s.email = 'ben.tey@outlook.com' s.homepage = 'https://github.com/btey/openproject-gitlab-integration' From d529bcf6314d403cb53db1d4a5682e4f642fc0cc Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:03:04 -0500 Subject: [PATCH 58/69] Add support for system hook to allow for global push events. --- lib/open_project/gitlab_integration/hook_handler.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/open_project/gitlab_integration/hook_handler.rb b/lib/open_project/gitlab_integration/hook_handler.rb index 0476494c5447..27a0d0853561 100644 --- a/lib/open_project/gitlab_integration/hook_handler.rb +++ b/lib/open_project/gitlab_integration/hook_handler.rb @@ -36,6 +36,7 @@ class HookHandler note_hook merge_request_hook pipeline_hook + system_hook ].freeze # A gitlab webhook happened. From 4f78fe34ad185a9cafbbd72cbbe8d864cf855eed Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:05:22 -0500 Subject: [PATCH 59/69] Clone push hook to system hook to allow for global pushes. --- .../notification_handler/system_hook.rb | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 lib/open_project/gitlab_integration/notification_handler/system_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/system_hook.rb b/lib/open_project/gitlab_integration/notification_handler/system_hook.rb new file mode 100644 index 000000000000..b573cacf351a --- /dev/null +++ b/lib/open_project/gitlab_integration/notification_handler/system_hook.rb @@ -0,0 +1,67 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2023 Ben Tey +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# Copyright (C) 2012-2021 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See docs/COPYRIGHT.rdoc for more details. +#++ + +module OpenProject::GitlabIntegration + module NotificationHandler + ## + # Handles Gitlab commit notifications. + class PushHook + include OpenProject::GitlabIntegration::NotificationHandler::Helper + + def process(payload_params) + @payload = wrap_payload(payload_params) + return nil unless payload.object_kind == 'push' + payload.commits.each do |commit| + user = User.find_by_id(payload.open_project_user_id) + text = commit['title'] + " - " + commit['message'] + work_packages = find_mentioned_work_packages(text, user) + notes = generate_notes(commit, payload) + comment_on_referenced_work_packages(work_packages, user, notes) + end + end + + private + + attr_reader :payload + + def generate_notes(commit, payload) + commit_id = commit['id'] + I18n.t("gitlab_integration.push_single_commit_comment", + :commit_number => commit_id[0, 8], + :commit_note => commit['message'], + :commit_url => commit['url'], + :commit_timestamp => commit['timestamp'], + :repository => payload.repository.name, + :repository_url => payload.repository.homepage, + :gitlab_user => payload.user_name, + :gitlab_user_url => payload.user_avatar) + end + end + end +end From 56ba8aa603998d27a2f68b4ee62c2ea7071b6e99 Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:13:18 -0500 Subject: [PATCH 60/69] Relabel class. --- .../gitlab_integration/notification_handler/system_hook.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_project/gitlab_integration/notification_handler/system_hook.rb b/lib/open_project/gitlab_integration/notification_handler/system_hook.rb index b573cacf351a..a23489a7ec3f 100644 --- a/lib/open_project/gitlab_integration/notification_handler/system_hook.rb +++ b/lib/open_project/gitlab_integration/notification_handler/system_hook.rb @@ -31,7 +31,7 @@ module OpenProject::GitlabIntegration module NotificationHandler ## # Handles Gitlab commit notifications. - class PushHook + class SystemHook include OpenProject::GitlabIntegration::NotificationHandler::Helper def process(payload_params) From 43931643e56cb34d51bdd97bc991b719a1f86ee6 Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:27:32 -0500 Subject: [PATCH 61/69] Add notification handler. --- .../gitlab_integration/notification_handlers.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/lib/open_project/gitlab_integration/notification_handlers.rb index 4acd6c6c0724..07993868c2ee 100644 --- a/lib/open_project/gitlab_integration/notification_handlers.rb +++ b/lib/open_project/gitlab_integration/notification_handlers.rb @@ -69,6 +69,12 @@ def pipeline_hook(payload) end end + def pipeline_hook(payload) + with_logging('system_hook') do + OpenProject::GitlabIntegration::NotificationHandler::SystemHook.new.process(payload) + end + end + private def with_logging(event_hook) From 3d7eb27f769fb2568344a344864b531ee92bc6b6 Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:28:34 -0500 Subject: [PATCH 62/69] Update initializer. --- lib/open_project/gitlab_integration/engine.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/open_project/gitlab_integration/engine.rb b/lib/open_project/gitlab_integration/engine.rb index 752d2cb42c0a..e1ee33b1afa5 100644 --- a/lib/open_project/gitlab_integration/engine.rb +++ b/lib/open_project/gitlab_integration/engine.rb @@ -69,6 +69,8 @@ class Engine < ::Rails::Engine &NotificationHandlers.method(:push_hook)) ::OpenProject::Notifications.subscribe('gitlab.pipeline_hook', &NotificationHandlers.method(:pipeline_hook)) + ::OpenProject::Notifications.subscribe('gitlab.system_hook', + &NotificationHandlers.method(:system_hook)) end extend_api_response(:v3, :work_packages, :work_package, From aa27ba06fa16d08ab54d53e1f5c29e7650bb1fa5 Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:32:09 -0500 Subject: [PATCH 63/69] Add require --- lib/open_project/gitlab_integration/notification_handlers.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/lib/open_project/gitlab_integration/notification_handlers.rb index 07993868c2ee..3847e4408815 100644 --- a/lib/open_project/gitlab_integration/notification_handlers.rb +++ b/lib/open_project/gitlab_integration/notification_handlers.rb @@ -32,6 +32,7 @@ require_relative './notification_handler/merge_request_hook' require_relative './notification_handler/note_hook' require_relative './notification_handler/push_hook' +require_relative './notification_handler/system_hook' module OpenProject::GitlabIntegration From 66ee35b6e90a9d3010915a64caa90fa205081c96 Mon Sep 17 00:00:00 2001 From: WiredTombstone Date: Thu, 28 Dec 2023 22:39:45 -0500 Subject: [PATCH 64/69] Update notification_handlers.rb --- lib/open_project/gitlab_integration/notification_handlers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/lib/open_project/gitlab_integration/notification_handlers.rb index 3847e4408815..7c78293b6491 100644 --- a/lib/open_project/gitlab_integration/notification_handlers.rb +++ b/lib/open_project/gitlab_integration/notification_handlers.rb @@ -70,7 +70,7 @@ def pipeline_hook(payload) end end - def pipeline_hook(payload) + def system_hook(payload) with_logging('system_hook') do OpenProject::GitlabIntegration::NotificationHandler::SystemHook.new.process(payload) end From 097c47f725c497b59d4538b9ddd9d4696853f998 Mon Sep 17 00:00:00 2001 From: btey Date: Mon, 15 Jan 2024 17:10:06 -0500 Subject: [PATCH 65/69] Update README.md Changes in the readme to reflect the new system hooks push event capture. --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a39be3e20d45..73ac4d8d6dec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.4 GA +## Introducing OpenProject GitLab Integration v2.1 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -7,7 +7,7 @@ In this version it has also been implemented that **all linked or referenced** I If there are labels related to the Issue or MR, a button with the label icon will appear. By clicking the button you can show/hide the associated labels. - +

    @@ -15,7 +15,7 @@ If there are labels related to the Issue or MR, a button with the label icon wil OpenProject module for integration with GitLab: * Latest Gitlab release tested: **16.6.2** -* Latest OpenProject release tested: **13.1.0** (for OP v13.0.X use v2.1.2) +* Latest OpenProject release tested: **13.1.2** (for OP v13.0.X use v2.1.2) The reference system is based on the same system as for GitHub integration. You can use a link to the work package or just use “OP#87” or "PP#87" in the title/description of the Issue/MR in GitLab. @@ -32,6 +32,7 @@ OpenProject will **add comments** to work package for the following events: * Merge Request (Opened, Closed and Merged) * Issue (Opened, Closed) * Push commits in Merge Requests + * Since v2.1.5 of the plugin you can activate system hooks for Push events. * Comments (on Issues, Merge Request, Commits and Snippets) * Pipelines (Beta feature) @@ -159,7 +160,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1.4) + openproject-gitlab_integration (2.1.5) openproject-webhooks ``` @@ -213,11 +214,11 @@ You need to configure just two things in the webhook: 1. The URL must point to your OpenProject server’s GitLab webhook endpoint (/webhooks/gitlab). Append it to the URL as a simple GET parameter named key. In the end the URL should look something like this: ``` - http://openproject-url.com/webhooks/gitlab?key=ae278268 + http://openproject-url.com/webhooks/gitlab?key=[previous_generated_access_token_key] ``` 2. Enable the required triggers: - 1. Push events + 1. Push events (project hooks or systems hooks) 2. Comments 3. Issues events 4. Merge request events From cc83cfabbf1e417d007d452ab02a9d8f63fe9095 Mon Sep 17 00:00:00 2001 From: btey Date: Mon, 15 Jan 2024 18:45:09 -0500 Subject: [PATCH 66/69] Update README.md Adding JetBrains support logo. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 73ac4d8d6dec..a89abd61930f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## Introducing OpenProject GitLab Integration v2.1 GA +
    + Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). In this version it has also been implemented that **all linked or referenced** Issues appear in the GitLab tab (https://github.com/btey/openproject-gitlab-integration/issues/34). The opportunity has also been taken to redesign how the information is presented so that it is visually easy to read and at the same time can continue to provide all the information, including labels and pipeline status. @@ -10,7 +12,6 @@ If there are labels related to the Issue or MR, a button with the label icon wil

    - ## Overview OpenProject module for integration with GitLab: From 6cf6aa070a406e4d77d1af9b7401c18a2493d16f Mon Sep 17 00:00:00 2001 From: btey Date: Thu, 15 Feb 2024 10:24:47 -0500 Subject: [PATCH 67/69] Update README.md Changing version to 2.1.6 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ccbc0e9ccd4..37a69389326b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Introducing OpenProject GitLab Integration v2.1.5 GA +## Introducing OpenProject GitLab Integration v2.1.6 GA Based on the OpenProject Github Integration, this plugin offers the same functionalities plus other new features. This is the first version that includes the visualization of the status of the *Pipelines* (by now, it is considered in Beta status). You can test it by activating the Pipelines event in the GitLab webhook. Just keep in mind that not all pipelines will be reflected in OpenProject, only Merge Request type pipelines (for more information see the GitLab issue https://gitlab.com/gitlab-org/gitlab/-/issues/345028). Any feedback about the pipelines feature would be very appreciated, whether it works or if issues arise (you can use this ticket https://github.com/btey/openproject-gitlab-integration/issues/43). @@ -159,7 +159,7 @@ Add the following in **Gemfile.lock**: PATH remote: modules/gitlab_integration specs: - openproject-gitlab_integration (2.1.5) + openproject-gitlab_integration (2.1.6) openproject-webhooks ``` From 0e31437d365c334dc22fda92e38a602684fbfc28 Mon Sep 17 00:00:00 2001 From: btey Date: Thu, 15 Feb 2024 10:25:57 -0500 Subject: [PATCH 68/69] Update openproject-gitlab_integration.gemspec Changing version to 2.1.6 --- openproject-gitlab_integration.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openproject-gitlab_integration.gemspec b/openproject-gitlab_integration.gemspec index 8acf7073c24a..f26ebc622261 100644 --- a/openproject-gitlab_integration.gemspec +++ b/openproject-gitlab_integration.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |s| s.name = 'openproject-gitlab_integration' - s.version = '2.1.5' + s.version = '2.1.6' s.authors = 'Ben Tey' s.email = 'ben.tey@outlook.com' s.homepage = 'https://github.com/btey/openproject-gitlab-integration' From 3020d70f6649a2dae184442112ace5142cc2e029 Mon Sep 17 00:00:00 2001 From: Kabiru Mwenja Date: Wed, 28 Feb 2024 16:25:47 +0300 Subject: [PATCH 69/69] [Op#53019] Bring btey/openproject-gitlab-integration into a structure that matches openproject https://community.openproject.org/work_packages/53019 --- LICENSE => modules/gitlab_integration/LICENSE | 0 README.md => modules/gitlab_integration/README.md | 0 .../gitlab_integration/app}/models/gitlab_issue.rb | 0 .../app}/models/gitlab_merge_request.rb | 0 .../app}/models/gitlab_pipeline.rb | 0 .../gitlab_integration/app}/models/gitlab_user.rb | 0 .../workers/cron/clear_old_merge_requests_job.rb | 0 .../config}/locales/crowdin/ar.yml | 0 .../config}/locales/crowdin/bg.yml | 0 .../config}/locales/crowdin/ca.yml | 0 .../config}/locales/crowdin/cs.yml | 0 .../config}/locales/crowdin/da.yml | 0 .../config}/locales/crowdin/de.yml | 0 .../config}/locales/crowdin/el.yml | 0 .../config}/locales/crowdin/es.yml | 0 .../config}/locales/crowdin/fi.yml | 0 .../config}/locales/crowdin/fil.yml | 0 .../config}/locales/crowdin/fr.yml | 0 .../config}/locales/crowdin/hr.yml | 0 .../config}/locales/crowdin/hu.yml | 0 .../config}/locales/crowdin/id.yml | 0 .../config}/locales/crowdin/it.yml | 0 .../config}/locales/crowdin/ja.yml | 0 .../config}/locales/crowdin/js-ar.yml | 0 .../config}/locales/crowdin/js-bg.yml | 0 .../config}/locales/crowdin/js-ca.yml | 0 .../config}/locales/crowdin/js-cs.yml | 0 .../config}/locales/crowdin/js-da.yml | 0 .../config}/locales/crowdin/js-de.yml | 0 .../config}/locales/crowdin/js-el.yml | 0 .../config}/locales/crowdin/js-es.yml | 0 .../config}/locales/crowdin/js-fi.yml | 0 .../config}/locales/crowdin/js-fil.yml | 0 .../config}/locales/crowdin/js-fr.yml | 0 .../config}/locales/crowdin/js-hr.yml | 0 .../config}/locales/crowdin/js-hu.yml | 0 .../config}/locales/crowdin/js-id.yml | 0 .../config}/locales/crowdin/js-it.yml | 0 .../config}/locales/crowdin/js-ja.yml | 0 .../config}/locales/crowdin/js-ko.yml | 0 .../config}/locales/crowdin/js-lt.yml | 0 .../config}/locales/crowdin/js-nl.yml | 0 .../config}/locales/crowdin/js-no.yml | 0 .../config}/locales/crowdin/js-pl.yml | 0 .../config}/locales/crowdin/js-pt.yml | 0 .../config}/locales/crowdin/js-ro.yml | 0 .../config}/locales/crowdin/js-ru.yml | 0 .../config}/locales/crowdin/js-sk.yml | 0 .../config}/locales/crowdin/js-sl.yml | 0 .../config}/locales/crowdin/js-sv.yml | 0 .../config}/locales/crowdin/js-tr.yml | 0 .../config}/locales/crowdin/js-uk.yml | 0 .../config}/locales/crowdin/js-vi.yml | 0 .../config}/locales/crowdin/js-zh-CN.yml | 0 .../config}/locales/crowdin/js-zh-TW.yml | 0 .../config}/locales/crowdin/ko.yml | 0 .../config}/locales/crowdin/lt.yml | 0 .../config}/locales/crowdin/nl.yml | 0 .../config}/locales/crowdin/no.yml | 0 .../config}/locales/crowdin/pl.yml | 0 .../config}/locales/crowdin/pt.yml | 0 .../config}/locales/crowdin/ro.yml | 0 .../config}/locales/crowdin/ru.yml | 0 .../config}/locales/crowdin/sk.yml | 0 .../config}/locales/crowdin/sl.yml | 0 .../config}/locales/crowdin/sv.yml | 0 .../config}/locales/crowdin/tr.yml | 0 .../config}/locales/crowdin/uk.yml | 0 .../config}/locales/crowdin/vi.yml | 0 .../config}/locales/crowdin/zh-CN.yml | 0 .../config}/locales/crowdin/zh-TW.yml | 0 .../gitlab_integration/config}/locales/de.yml | 0 .../gitlab_integration/config}/locales/en.yml | 0 .../gitlab_integration/config}/locales/js-en.yml | 0 .../20211015110000_gitlab_integration_models.rb | 0 ...211015110001_add_username_commit_to_pipelines.rb | 0 .../db}/migrate/20211015110002_add_gitlab_issues.rb | 0 .../doc}/op-commented-in-issue.png | Bin .../gitlab_integration/doc}/op-commented-in-mr.png | Bin .../gitlab_integration/doc}/op-issue-opened.png | Bin .../doc}/op-mr-merged-event-1.png | Bin .../doc}/op-mr-merged-event-2.png | Bin .../doc}/op-mr-merged-event-3.png | Bin .../doc}/op-mr-merged-event-4.png | Bin .../gitlab_integration/doc}/op-mr-opened.png | Bin .../gitlab_integration/doc}/op-pushed-in-mr.png | Bin .../doc}/op-referenced-in-commit.png | Bin .../doc}/op-referenced-in-issue.png | Bin .../git-actions-menu/git-actions-menu.component.ts | 0 .../git-actions-menu/git-actions-menu.directive.ts | 0 .../git-actions-menu/git-actions-menu.template.html | 0 .../git-actions-menu/styles/git-actions-menu.sass | 0 .../module/git-actions/git-actions.service.ts | 0 .../module/gitlab-tab/gitlab-tab.component.sass | 0 .../module/gitlab-tab/gitlab-tab.component.ts | 0 .../module/gitlab-tab/gitlab-tab.template.html | 0 .../module/hal/resources/gitlab-issue-resource.ts | 0 .../hal/resources/gitlab-merge-request-resource.ts | 0 .../module/hal/resources/gitlab-user-resource.ts | 0 .../frontend}/module/icons/gitlab-icons.svg | 0 .../frontend}/module/issue/issue.component.html | 0 .../frontend}/module/issue/issue.component.sass | 0 .../frontend}/module/issue/issue.component.ts | 0 .../gitlab_integration/frontend}/module/main.ts | 0 .../merge-request/merge-request.component.html | 0 .../merge-request/merge-request.component.sass | 0 .../module/merge-request/merge-request.component.ts | 0 .../module/merge-request/mr-pipeline.component.sass | 0 .../tab-header-issue/styles/tab-header-issue.sass | 0 .../tab-header-issue/tab-header-issue.component.ts | 0 .../tab-header-issue/tab-header-issue.template.html | 0 .../module/tab-header-mr/styles/tab-header-mr.sass | 0 .../module/tab-header-mr/tab-header-mr.component.ts | 0 .../tab-header-mr/tab-header-mr.template.html | 0 .../module/tab-issue/tab-issue.component.ts | 0 .../module/tab-issue/tab-issue.template.html | 0 .../module/tab-issue/wp-gitlab-issue.service.ts | 0 .../frontend}/module/tab-mrs/tab-mrs.component.ts | 0 .../frontend}/module/tab-mrs/tab-mrs.template.html | 0 .../module/tab-mrs/wp-gitlab-mrs.service.ts | 0 .../frontend}/module/typings.d.ts | 0 .../gitlab_issue_collection_representer.rb | 0 .../v3/gitlab_issues/gitlab_issue_representer.rb | 0 .../gitlab_issues_by_work_package_api.rb | 0 .../api/v3/gitlab_issues/gitlab_user_representer.rb | 0 .../gitlab_merge_request_collection_representer.rb | 0 .../gitlab_merge_request_representer.rb | 0 .../gitlab_merge_requests_by_work_package_api.rb | 0 .../gitlab_pipeline_representer.rb | 0 .../gitlab_user_representer.rb | 0 .../lib}/open_project/gitlab_integration.rb | 0 .../lib}/open_project/gitlab_integration/engine.rb | 0 .../open_project/gitlab_integration/hook_handler.rb | 0 .../notification_handler/helper.rb | 0 .../notification_handler/issue_hook.rb | 0 .../notification_handler/merge_request_hook.rb | 0 .../notification_handler/note_hook.rb | 0 .../notification_handler/pipeline_hook.rb | 0 .../notification_handler/push_hook.rb | 0 .../notification_handler/system_hook.rb | 0 .../gitlab_integration/notification_handlers.rb | 0 .../patches/api/work_package_representer.rb | 0 .../patches/work_package_patch.rb | 0 .../open_project/gitlab_integration/services.rb | 0 .../gitlab_integration/services/params_helper.rb | 0 .../services/upsert_gitlab_user.rb | 0 .../gitlab_integration/services/upsert_issue.rb | 0 .../services/upsert_issue_note.rb | 0 .../services/upsert_merge_request.rb | 0 .../gitlab_integration/services/upsert_pipeline.rb | 0 .../lib}/openproject-gitlab_integration.rb | 0 .../openproject-gitlab_integration.gemspec | 0 152 files changed, 0 insertions(+), 0 deletions(-) rename LICENSE => modules/gitlab_integration/LICENSE (100%) rename README.md => modules/gitlab_integration/README.md (100%) rename {app => modules/gitlab_integration/app}/models/gitlab_issue.rb (100%) rename {app => modules/gitlab_integration/app}/models/gitlab_merge_request.rb (100%) rename {app => modules/gitlab_integration/app}/models/gitlab_pipeline.rb (100%) rename {app => modules/gitlab_integration/app}/models/gitlab_user.rb (100%) rename {app => modules/gitlab_integration/app}/workers/cron/clear_old_merge_requests_job.rb (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/ar.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/bg.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/ca.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/cs.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/da.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/de.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/el.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/es.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/fi.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/fil.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/fr.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/hr.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/hu.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/id.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/it.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/ja.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-ar.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-bg.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-ca.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-cs.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-da.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-de.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-el.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-es.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-fi.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-fil.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-fr.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-hr.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-hu.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-id.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-it.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-ja.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-ko.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-lt.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-nl.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-no.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-pl.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-pt.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-ro.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-ru.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-sk.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-sl.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-sv.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-tr.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-uk.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-vi.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-zh-CN.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/js-zh-TW.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/ko.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/lt.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/nl.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/no.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/pl.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/pt.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/ro.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/ru.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/sk.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/sl.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/sv.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/tr.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/uk.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/vi.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/zh-CN.yml (100%) rename {config => modules/gitlab_integration/config}/locales/crowdin/zh-TW.yml (100%) rename {config => modules/gitlab_integration/config}/locales/de.yml (100%) rename {config => modules/gitlab_integration/config}/locales/en.yml (100%) rename {config => modules/gitlab_integration/config}/locales/js-en.yml (100%) rename {db => modules/gitlab_integration/db}/migrate/20211015110000_gitlab_integration_models.rb (100%) rename {db => modules/gitlab_integration/db}/migrate/20211015110001_add_username_commit_to_pipelines.rb (100%) rename {db => modules/gitlab_integration/db}/migrate/20211015110002_add_gitlab_issues.rb (100%) rename {doc => modules/gitlab_integration/doc}/op-commented-in-issue.png (100%) rename {doc => modules/gitlab_integration/doc}/op-commented-in-mr.png (100%) rename {doc => modules/gitlab_integration/doc}/op-issue-opened.png (100%) rename {doc => modules/gitlab_integration/doc}/op-mr-merged-event-1.png (100%) rename {doc => modules/gitlab_integration/doc}/op-mr-merged-event-2.png (100%) rename {doc => modules/gitlab_integration/doc}/op-mr-merged-event-3.png (100%) rename {doc => modules/gitlab_integration/doc}/op-mr-merged-event-4.png (100%) rename {doc => modules/gitlab_integration/doc}/op-mr-opened.png (100%) rename {doc => modules/gitlab_integration/doc}/op-pushed-in-mr.png (100%) rename {doc => modules/gitlab_integration/doc}/op-referenced-in-commit.png (100%) rename {doc => modules/gitlab_integration/doc}/op-referenced-in-issue.png (100%) rename {frontend => modules/gitlab_integration/frontend}/module/git-actions-menu/git-actions-menu.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/git-actions-menu/git-actions-menu.directive.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/git-actions-menu/git-actions-menu.template.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/git-actions-menu/styles/git-actions-menu.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/git-actions/git-actions.service.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/gitlab-tab/gitlab-tab.component.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/gitlab-tab/gitlab-tab.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/gitlab-tab/gitlab-tab.template.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/hal/resources/gitlab-issue-resource.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/hal/resources/gitlab-merge-request-resource.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/hal/resources/gitlab-user-resource.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/icons/gitlab-icons.svg (100%) rename {frontend => modules/gitlab_integration/frontend}/module/issue/issue.component.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/issue/issue.component.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/issue/issue.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/main.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/merge-request/merge-request.component.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/merge-request/merge-request.component.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/merge-request/merge-request.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/merge-request/mr-pipeline.component.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-header-issue/styles/tab-header-issue.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-header-issue/tab-header-issue.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-header-issue/tab-header-issue.template.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-header-mr/styles/tab-header-mr.sass (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-header-mr/tab-header-mr.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-header-mr/tab-header-mr.template.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-issue/tab-issue.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-issue/tab-issue.template.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-issue/wp-gitlab-issue.service.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-mrs/tab-mrs.component.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-mrs/tab-mrs.template.html (100%) rename {frontend => modules/gitlab_integration/frontend}/module/tab-mrs/wp-gitlab-mrs.service.ts (100%) rename {frontend => modules/gitlab_integration/frontend}/module/typings.d.ts (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_issues/gitlab_issue_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_issues/gitlab_user_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/api/v3/gitlab_merge_requests/gitlab_user_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/engine.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/hook_handler.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/helper.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/issue_hook.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/merge_request_hook.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/note_hook.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/pipeline_hook.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/push_hook.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handler/system_hook.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/notification_handlers.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/patches/api/work_package_representer.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/patches/work_package_patch.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services/params_helper.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services/upsert_gitlab_user.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services/upsert_issue.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services/upsert_issue_note.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services/upsert_merge_request.rb (100%) rename {lib => modules/gitlab_integration/lib}/open_project/gitlab_integration/services/upsert_pipeline.rb (100%) rename {lib => modules/gitlab_integration/lib}/openproject-gitlab_integration.rb (100%) rename openproject-gitlab_integration.gemspec => modules/gitlab_integration/openproject-gitlab_integration.gemspec (100%) diff --git a/LICENSE b/modules/gitlab_integration/LICENSE similarity index 100% rename from LICENSE rename to modules/gitlab_integration/LICENSE diff --git a/README.md b/modules/gitlab_integration/README.md similarity index 100% rename from README.md rename to modules/gitlab_integration/README.md diff --git a/app/models/gitlab_issue.rb b/modules/gitlab_integration/app/models/gitlab_issue.rb similarity index 100% rename from app/models/gitlab_issue.rb rename to modules/gitlab_integration/app/models/gitlab_issue.rb diff --git a/app/models/gitlab_merge_request.rb b/modules/gitlab_integration/app/models/gitlab_merge_request.rb similarity index 100% rename from app/models/gitlab_merge_request.rb rename to modules/gitlab_integration/app/models/gitlab_merge_request.rb diff --git a/app/models/gitlab_pipeline.rb b/modules/gitlab_integration/app/models/gitlab_pipeline.rb similarity index 100% rename from app/models/gitlab_pipeline.rb rename to modules/gitlab_integration/app/models/gitlab_pipeline.rb diff --git a/app/models/gitlab_user.rb b/modules/gitlab_integration/app/models/gitlab_user.rb similarity index 100% rename from app/models/gitlab_user.rb rename to modules/gitlab_integration/app/models/gitlab_user.rb diff --git a/app/workers/cron/clear_old_merge_requests_job.rb b/modules/gitlab_integration/app/workers/cron/clear_old_merge_requests_job.rb similarity index 100% rename from app/workers/cron/clear_old_merge_requests_job.rb rename to modules/gitlab_integration/app/workers/cron/clear_old_merge_requests_job.rb diff --git a/config/locales/crowdin/ar.yml b/modules/gitlab_integration/config/locales/crowdin/ar.yml similarity index 100% rename from config/locales/crowdin/ar.yml rename to modules/gitlab_integration/config/locales/crowdin/ar.yml diff --git a/config/locales/crowdin/bg.yml b/modules/gitlab_integration/config/locales/crowdin/bg.yml similarity index 100% rename from config/locales/crowdin/bg.yml rename to modules/gitlab_integration/config/locales/crowdin/bg.yml diff --git a/config/locales/crowdin/ca.yml b/modules/gitlab_integration/config/locales/crowdin/ca.yml similarity index 100% rename from config/locales/crowdin/ca.yml rename to modules/gitlab_integration/config/locales/crowdin/ca.yml diff --git a/config/locales/crowdin/cs.yml b/modules/gitlab_integration/config/locales/crowdin/cs.yml similarity index 100% rename from config/locales/crowdin/cs.yml rename to modules/gitlab_integration/config/locales/crowdin/cs.yml diff --git a/config/locales/crowdin/da.yml b/modules/gitlab_integration/config/locales/crowdin/da.yml similarity index 100% rename from config/locales/crowdin/da.yml rename to modules/gitlab_integration/config/locales/crowdin/da.yml diff --git a/config/locales/crowdin/de.yml b/modules/gitlab_integration/config/locales/crowdin/de.yml similarity index 100% rename from config/locales/crowdin/de.yml rename to modules/gitlab_integration/config/locales/crowdin/de.yml diff --git a/config/locales/crowdin/el.yml b/modules/gitlab_integration/config/locales/crowdin/el.yml similarity index 100% rename from config/locales/crowdin/el.yml rename to modules/gitlab_integration/config/locales/crowdin/el.yml diff --git a/config/locales/crowdin/es.yml b/modules/gitlab_integration/config/locales/crowdin/es.yml similarity index 100% rename from config/locales/crowdin/es.yml rename to modules/gitlab_integration/config/locales/crowdin/es.yml diff --git a/config/locales/crowdin/fi.yml b/modules/gitlab_integration/config/locales/crowdin/fi.yml similarity index 100% rename from config/locales/crowdin/fi.yml rename to modules/gitlab_integration/config/locales/crowdin/fi.yml diff --git a/config/locales/crowdin/fil.yml b/modules/gitlab_integration/config/locales/crowdin/fil.yml similarity index 100% rename from config/locales/crowdin/fil.yml rename to modules/gitlab_integration/config/locales/crowdin/fil.yml diff --git a/config/locales/crowdin/fr.yml b/modules/gitlab_integration/config/locales/crowdin/fr.yml similarity index 100% rename from config/locales/crowdin/fr.yml rename to modules/gitlab_integration/config/locales/crowdin/fr.yml diff --git a/config/locales/crowdin/hr.yml b/modules/gitlab_integration/config/locales/crowdin/hr.yml similarity index 100% rename from config/locales/crowdin/hr.yml rename to modules/gitlab_integration/config/locales/crowdin/hr.yml diff --git a/config/locales/crowdin/hu.yml b/modules/gitlab_integration/config/locales/crowdin/hu.yml similarity index 100% rename from config/locales/crowdin/hu.yml rename to modules/gitlab_integration/config/locales/crowdin/hu.yml diff --git a/config/locales/crowdin/id.yml b/modules/gitlab_integration/config/locales/crowdin/id.yml similarity index 100% rename from config/locales/crowdin/id.yml rename to modules/gitlab_integration/config/locales/crowdin/id.yml diff --git a/config/locales/crowdin/it.yml b/modules/gitlab_integration/config/locales/crowdin/it.yml similarity index 100% rename from config/locales/crowdin/it.yml rename to modules/gitlab_integration/config/locales/crowdin/it.yml diff --git a/config/locales/crowdin/ja.yml b/modules/gitlab_integration/config/locales/crowdin/ja.yml similarity index 100% rename from config/locales/crowdin/ja.yml rename to modules/gitlab_integration/config/locales/crowdin/ja.yml diff --git a/config/locales/crowdin/js-ar.yml b/modules/gitlab_integration/config/locales/crowdin/js-ar.yml similarity index 100% rename from config/locales/crowdin/js-ar.yml rename to modules/gitlab_integration/config/locales/crowdin/js-ar.yml diff --git a/config/locales/crowdin/js-bg.yml b/modules/gitlab_integration/config/locales/crowdin/js-bg.yml similarity index 100% rename from config/locales/crowdin/js-bg.yml rename to modules/gitlab_integration/config/locales/crowdin/js-bg.yml diff --git a/config/locales/crowdin/js-ca.yml b/modules/gitlab_integration/config/locales/crowdin/js-ca.yml similarity index 100% rename from config/locales/crowdin/js-ca.yml rename to modules/gitlab_integration/config/locales/crowdin/js-ca.yml diff --git a/config/locales/crowdin/js-cs.yml b/modules/gitlab_integration/config/locales/crowdin/js-cs.yml similarity index 100% rename from config/locales/crowdin/js-cs.yml rename to modules/gitlab_integration/config/locales/crowdin/js-cs.yml diff --git a/config/locales/crowdin/js-da.yml b/modules/gitlab_integration/config/locales/crowdin/js-da.yml similarity index 100% rename from config/locales/crowdin/js-da.yml rename to modules/gitlab_integration/config/locales/crowdin/js-da.yml diff --git a/config/locales/crowdin/js-de.yml b/modules/gitlab_integration/config/locales/crowdin/js-de.yml similarity index 100% rename from config/locales/crowdin/js-de.yml rename to modules/gitlab_integration/config/locales/crowdin/js-de.yml diff --git a/config/locales/crowdin/js-el.yml b/modules/gitlab_integration/config/locales/crowdin/js-el.yml similarity index 100% rename from config/locales/crowdin/js-el.yml rename to modules/gitlab_integration/config/locales/crowdin/js-el.yml diff --git a/config/locales/crowdin/js-es.yml b/modules/gitlab_integration/config/locales/crowdin/js-es.yml similarity index 100% rename from config/locales/crowdin/js-es.yml rename to modules/gitlab_integration/config/locales/crowdin/js-es.yml diff --git a/config/locales/crowdin/js-fi.yml b/modules/gitlab_integration/config/locales/crowdin/js-fi.yml similarity index 100% rename from config/locales/crowdin/js-fi.yml rename to modules/gitlab_integration/config/locales/crowdin/js-fi.yml diff --git a/config/locales/crowdin/js-fil.yml b/modules/gitlab_integration/config/locales/crowdin/js-fil.yml similarity index 100% rename from config/locales/crowdin/js-fil.yml rename to modules/gitlab_integration/config/locales/crowdin/js-fil.yml diff --git a/config/locales/crowdin/js-fr.yml b/modules/gitlab_integration/config/locales/crowdin/js-fr.yml similarity index 100% rename from config/locales/crowdin/js-fr.yml rename to modules/gitlab_integration/config/locales/crowdin/js-fr.yml diff --git a/config/locales/crowdin/js-hr.yml b/modules/gitlab_integration/config/locales/crowdin/js-hr.yml similarity index 100% rename from config/locales/crowdin/js-hr.yml rename to modules/gitlab_integration/config/locales/crowdin/js-hr.yml diff --git a/config/locales/crowdin/js-hu.yml b/modules/gitlab_integration/config/locales/crowdin/js-hu.yml similarity index 100% rename from config/locales/crowdin/js-hu.yml rename to modules/gitlab_integration/config/locales/crowdin/js-hu.yml diff --git a/config/locales/crowdin/js-id.yml b/modules/gitlab_integration/config/locales/crowdin/js-id.yml similarity index 100% rename from config/locales/crowdin/js-id.yml rename to modules/gitlab_integration/config/locales/crowdin/js-id.yml diff --git a/config/locales/crowdin/js-it.yml b/modules/gitlab_integration/config/locales/crowdin/js-it.yml similarity index 100% rename from config/locales/crowdin/js-it.yml rename to modules/gitlab_integration/config/locales/crowdin/js-it.yml diff --git a/config/locales/crowdin/js-ja.yml b/modules/gitlab_integration/config/locales/crowdin/js-ja.yml similarity index 100% rename from config/locales/crowdin/js-ja.yml rename to modules/gitlab_integration/config/locales/crowdin/js-ja.yml diff --git a/config/locales/crowdin/js-ko.yml b/modules/gitlab_integration/config/locales/crowdin/js-ko.yml similarity index 100% rename from config/locales/crowdin/js-ko.yml rename to modules/gitlab_integration/config/locales/crowdin/js-ko.yml diff --git a/config/locales/crowdin/js-lt.yml b/modules/gitlab_integration/config/locales/crowdin/js-lt.yml similarity index 100% rename from config/locales/crowdin/js-lt.yml rename to modules/gitlab_integration/config/locales/crowdin/js-lt.yml diff --git a/config/locales/crowdin/js-nl.yml b/modules/gitlab_integration/config/locales/crowdin/js-nl.yml similarity index 100% rename from config/locales/crowdin/js-nl.yml rename to modules/gitlab_integration/config/locales/crowdin/js-nl.yml diff --git a/config/locales/crowdin/js-no.yml b/modules/gitlab_integration/config/locales/crowdin/js-no.yml similarity index 100% rename from config/locales/crowdin/js-no.yml rename to modules/gitlab_integration/config/locales/crowdin/js-no.yml diff --git a/config/locales/crowdin/js-pl.yml b/modules/gitlab_integration/config/locales/crowdin/js-pl.yml similarity index 100% rename from config/locales/crowdin/js-pl.yml rename to modules/gitlab_integration/config/locales/crowdin/js-pl.yml diff --git a/config/locales/crowdin/js-pt.yml b/modules/gitlab_integration/config/locales/crowdin/js-pt.yml similarity index 100% rename from config/locales/crowdin/js-pt.yml rename to modules/gitlab_integration/config/locales/crowdin/js-pt.yml diff --git a/config/locales/crowdin/js-ro.yml b/modules/gitlab_integration/config/locales/crowdin/js-ro.yml similarity index 100% rename from config/locales/crowdin/js-ro.yml rename to modules/gitlab_integration/config/locales/crowdin/js-ro.yml diff --git a/config/locales/crowdin/js-ru.yml b/modules/gitlab_integration/config/locales/crowdin/js-ru.yml similarity index 100% rename from config/locales/crowdin/js-ru.yml rename to modules/gitlab_integration/config/locales/crowdin/js-ru.yml diff --git a/config/locales/crowdin/js-sk.yml b/modules/gitlab_integration/config/locales/crowdin/js-sk.yml similarity index 100% rename from config/locales/crowdin/js-sk.yml rename to modules/gitlab_integration/config/locales/crowdin/js-sk.yml diff --git a/config/locales/crowdin/js-sl.yml b/modules/gitlab_integration/config/locales/crowdin/js-sl.yml similarity index 100% rename from config/locales/crowdin/js-sl.yml rename to modules/gitlab_integration/config/locales/crowdin/js-sl.yml diff --git a/config/locales/crowdin/js-sv.yml b/modules/gitlab_integration/config/locales/crowdin/js-sv.yml similarity index 100% rename from config/locales/crowdin/js-sv.yml rename to modules/gitlab_integration/config/locales/crowdin/js-sv.yml diff --git a/config/locales/crowdin/js-tr.yml b/modules/gitlab_integration/config/locales/crowdin/js-tr.yml similarity index 100% rename from config/locales/crowdin/js-tr.yml rename to modules/gitlab_integration/config/locales/crowdin/js-tr.yml diff --git a/config/locales/crowdin/js-uk.yml b/modules/gitlab_integration/config/locales/crowdin/js-uk.yml similarity index 100% rename from config/locales/crowdin/js-uk.yml rename to modules/gitlab_integration/config/locales/crowdin/js-uk.yml diff --git a/config/locales/crowdin/js-vi.yml b/modules/gitlab_integration/config/locales/crowdin/js-vi.yml similarity index 100% rename from config/locales/crowdin/js-vi.yml rename to modules/gitlab_integration/config/locales/crowdin/js-vi.yml diff --git a/config/locales/crowdin/js-zh-CN.yml b/modules/gitlab_integration/config/locales/crowdin/js-zh-CN.yml similarity index 100% rename from config/locales/crowdin/js-zh-CN.yml rename to modules/gitlab_integration/config/locales/crowdin/js-zh-CN.yml diff --git a/config/locales/crowdin/js-zh-TW.yml b/modules/gitlab_integration/config/locales/crowdin/js-zh-TW.yml similarity index 100% rename from config/locales/crowdin/js-zh-TW.yml rename to modules/gitlab_integration/config/locales/crowdin/js-zh-TW.yml diff --git a/config/locales/crowdin/ko.yml b/modules/gitlab_integration/config/locales/crowdin/ko.yml similarity index 100% rename from config/locales/crowdin/ko.yml rename to modules/gitlab_integration/config/locales/crowdin/ko.yml diff --git a/config/locales/crowdin/lt.yml b/modules/gitlab_integration/config/locales/crowdin/lt.yml similarity index 100% rename from config/locales/crowdin/lt.yml rename to modules/gitlab_integration/config/locales/crowdin/lt.yml diff --git a/config/locales/crowdin/nl.yml b/modules/gitlab_integration/config/locales/crowdin/nl.yml similarity index 100% rename from config/locales/crowdin/nl.yml rename to modules/gitlab_integration/config/locales/crowdin/nl.yml diff --git a/config/locales/crowdin/no.yml b/modules/gitlab_integration/config/locales/crowdin/no.yml similarity index 100% rename from config/locales/crowdin/no.yml rename to modules/gitlab_integration/config/locales/crowdin/no.yml diff --git a/config/locales/crowdin/pl.yml b/modules/gitlab_integration/config/locales/crowdin/pl.yml similarity index 100% rename from config/locales/crowdin/pl.yml rename to modules/gitlab_integration/config/locales/crowdin/pl.yml diff --git a/config/locales/crowdin/pt.yml b/modules/gitlab_integration/config/locales/crowdin/pt.yml similarity index 100% rename from config/locales/crowdin/pt.yml rename to modules/gitlab_integration/config/locales/crowdin/pt.yml diff --git a/config/locales/crowdin/ro.yml b/modules/gitlab_integration/config/locales/crowdin/ro.yml similarity index 100% rename from config/locales/crowdin/ro.yml rename to modules/gitlab_integration/config/locales/crowdin/ro.yml diff --git a/config/locales/crowdin/ru.yml b/modules/gitlab_integration/config/locales/crowdin/ru.yml similarity index 100% rename from config/locales/crowdin/ru.yml rename to modules/gitlab_integration/config/locales/crowdin/ru.yml diff --git a/config/locales/crowdin/sk.yml b/modules/gitlab_integration/config/locales/crowdin/sk.yml similarity index 100% rename from config/locales/crowdin/sk.yml rename to modules/gitlab_integration/config/locales/crowdin/sk.yml diff --git a/config/locales/crowdin/sl.yml b/modules/gitlab_integration/config/locales/crowdin/sl.yml similarity index 100% rename from config/locales/crowdin/sl.yml rename to modules/gitlab_integration/config/locales/crowdin/sl.yml diff --git a/config/locales/crowdin/sv.yml b/modules/gitlab_integration/config/locales/crowdin/sv.yml similarity index 100% rename from config/locales/crowdin/sv.yml rename to modules/gitlab_integration/config/locales/crowdin/sv.yml diff --git a/config/locales/crowdin/tr.yml b/modules/gitlab_integration/config/locales/crowdin/tr.yml similarity index 100% rename from config/locales/crowdin/tr.yml rename to modules/gitlab_integration/config/locales/crowdin/tr.yml diff --git a/config/locales/crowdin/uk.yml b/modules/gitlab_integration/config/locales/crowdin/uk.yml similarity index 100% rename from config/locales/crowdin/uk.yml rename to modules/gitlab_integration/config/locales/crowdin/uk.yml diff --git a/config/locales/crowdin/vi.yml b/modules/gitlab_integration/config/locales/crowdin/vi.yml similarity index 100% rename from config/locales/crowdin/vi.yml rename to modules/gitlab_integration/config/locales/crowdin/vi.yml diff --git a/config/locales/crowdin/zh-CN.yml b/modules/gitlab_integration/config/locales/crowdin/zh-CN.yml similarity index 100% rename from config/locales/crowdin/zh-CN.yml rename to modules/gitlab_integration/config/locales/crowdin/zh-CN.yml diff --git a/config/locales/crowdin/zh-TW.yml b/modules/gitlab_integration/config/locales/crowdin/zh-TW.yml similarity index 100% rename from config/locales/crowdin/zh-TW.yml rename to modules/gitlab_integration/config/locales/crowdin/zh-TW.yml diff --git a/config/locales/de.yml b/modules/gitlab_integration/config/locales/de.yml similarity index 100% rename from config/locales/de.yml rename to modules/gitlab_integration/config/locales/de.yml diff --git a/config/locales/en.yml b/modules/gitlab_integration/config/locales/en.yml similarity index 100% rename from config/locales/en.yml rename to modules/gitlab_integration/config/locales/en.yml diff --git a/config/locales/js-en.yml b/modules/gitlab_integration/config/locales/js-en.yml similarity index 100% rename from config/locales/js-en.yml rename to modules/gitlab_integration/config/locales/js-en.yml diff --git a/db/migrate/20211015110000_gitlab_integration_models.rb b/modules/gitlab_integration/db/migrate/20211015110000_gitlab_integration_models.rb similarity index 100% rename from db/migrate/20211015110000_gitlab_integration_models.rb rename to modules/gitlab_integration/db/migrate/20211015110000_gitlab_integration_models.rb diff --git a/db/migrate/20211015110001_add_username_commit_to_pipelines.rb b/modules/gitlab_integration/db/migrate/20211015110001_add_username_commit_to_pipelines.rb similarity index 100% rename from db/migrate/20211015110001_add_username_commit_to_pipelines.rb rename to modules/gitlab_integration/db/migrate/20211015110001_add_username_commit_to_pipelines.rb diff --git a/db/migrate/20211015110002_add_gitlab_issues.rb b/modules/gitlab_integration/db/migrate/20211015110002_add_gitlab_issues.rb similarity index 100% rename from db/migrate/20211015110002_add_gitlab_issues.rb rename to modules/gitlab_integration/db/migrate/20211015110002_add_gitlab_issues.rb diff --git a/doc/op-commented-in-issue.png b/modules/gitlab_integration/doc/op-commented-in-issue.png similarity index 100% rename from doc/op-commented-in-issue.png rename to modules/gitlab_integration/doc/op-commented-in-issue.png diff --git a/doc/op-commented-in-mr.png b/modules/gitlab_integration/doc/op-commented-in-mr.png similarity index 100% rename from doc/op-commented-in-mr.png rename to modules/gitlab_integration/doc/op-commented-in-mr.png diff --git a/doc/op-issue-opened.png b/modules/gitlab_integration/doc/op-issue-opened.png similarity index 100% rename from doc/op-issue-opened.png rename to modules/gitlab_integration/doc/op-issue-opened.png diff --git a/doc/op-mr-merged-event-1.png b/modules/gitlab_integration/doc/op-mr-merged-event-1.png similarity index 100% rename from doc/op-mr-merged-event-1.png rename to modules/gitlab_integration/doc/op-mr-merged-event-1.png diff --git a/doc/op-mr-merged-event-2.png b/modules/gitlab_integration/doc/op-mr-merged-event-2.png similarity index 100% rename from doc/op-mr-merged-event-2.png rename to modules/gitlab_integration/doc/op-mr-merged-event-2.png diff --git a/doc/op-mr-merged-event-3.png b/modules/gitlab_integration/doc/op-mr-merged-event-3.png similarity index 100% rename from doc/op-mr-merged-event-3.png rename to modules/gitlab_integration/doc/op-mr-merged-event-3.png diff --git a/doc/op-mr-merged-event-4.png b/modules/gitlab_integration/doc/op-mr-merged-event-4.png similarity index 100% rename from doc/op-mr-merged-event-4.png rename to modules/gitlab_integration/doc/op-mr-merged-event-4.png diff --git a/doc/op-mr-opened.png b/modules/gitlab_integration/doc/op-mr-opened.png similarity index 100% rename from doc/op-mr-opened.png rename to modules/gitlab_integration/doc/op-mr-opened.png diff --git a/doc/op-pushed-in-mr.png b/modules/gitlab_integration/doc/op-pushed-in-mr.png similarity index 100% rename from doc/op-pushed-in-mr.png rename to modules/gitlab_integration/doc/op-pushed-in-mr.png diff --git a/doc/op-referenced-in-commit.png b/modules/gitlab_integration/doc/op-referenced-in-commit.png similarity index 100% rename from doc/op-referenced-in-commit.png rename to modules/gitlab_integration/doc/op-referenced-in-commit.png diff --git a/doc/op-referenced-in-issue.png b/modules/gitlab_integration/doc/op-referenced-in-issue.png similarity index 100% rename from doc/op-referenced-in-issue.png rename to modules/gitlab_integration/doc/op-referenced-in-issue.png diff --git a/frontend/module/git-actions-menu/git-actions-menu.component.ts b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts similarity index 100% rename from frontend/module/git-actions-menu/git-actions-menu.component.ts rename to modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.component.ts diff --git a/frontend/module/git-actions-menu/git-actions-menu.directive.ts b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts similarity index 100% rename from frontend/module/git-actions-menu/git-actions-menu.directive.ts rename to modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.directive.ts diff --git a/frontend/module/git-actions-menu/git-actions-menu.template.html b/modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.template.html similarity index 100% rename from frontend/module/git-actions-menu/git-actions-menu.template.html rename to modules/gitlab_integration/frontend/module/git-actions-menu/git-actions-menu.template.html diff --git a/frontend/module/git-actions-menu/styles/git-actions-menu.sass b/modules/gitlab_integration/frontend/module/git-actions-menu/styles/git-actions-menu.sass similarity index 100% rename from frontend/module/git-actions-menu/styles/git-actions-menu.sass rename to modules/gitlab_integration/frontend/module/git-actions-menu/styles/git-actions-menu.sass diff --git a/frontend/module/git-actions/git-actions.service.ts b/modules/gitlab_integration/frontend/module/git-actions/git-actions.service.ts similarity index 100% rename from frontend/module/git-actions/git-actions.service.ts rename to modules/gitlab_integration/frontend/module/git-actions/git-actions.service.ts diff --git a/frontend/module/gitlab-tab/gitlab-tab.component.sass b/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.sass similarity index 100% rename from frontend/module/gitlab-tab/gitlab-tab.component.sass rename to modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.sass diff --git a/frontend/module/gitlab-tab/gitlab-tab.component.ts b/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.ts similarity index 100% rename from frontend/module/gitlab-tab/gitlab-tab.component.ts rename to modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.component.ts diff --git a/frontend/module/gitlab-tab/gitlab-tab.template.html b/modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.template.html similarity index 100% rename from frontend/module/gitlab-tab/gitlab-tab.template.html rename to modules/gitlab_integration/frontend/module/gitlab-tab/gitlab-tab.template.html diff --git a/frontend/module/hal/resources/gitlab-issue-resource.ts b/modules/gitlab_integration/frontend/module/hal/resources/gitlab-issue-resource.ts similarity index 100% rename from frontend/module/hal/resources/gitlab-issue-resource.ts rename to modules/gitlab_integration/frontend/module/hal/resources/gitlab-issue-resource.ts diff --git a/frontend/module/hal/resources/gitlab-merge-request-resource.ts b/modules/gitlab_integration/frontend/module/hal/resources/gitlab-merge-request-resource.ts similarity index 100% rename from frontend/module/hal/resources/gitlab-merge-request-resource.ts rename to modules/gitlab_integration/frontend/module/hal/resources/gitlab-merge-request-resource.ts diff --git a/frontend/module/hal/resources/gitlab-user-resource.ts b/modules/gitlab_integration/frontend/module/hal/resources/gitlab-user-resource.ts similarity index 100% rename from frontend/module/hal/resources/gitlab-user-resource.ts rename to modules/gitlab_integration/frontend/module/hal/resources/gitlab-user-resource.ts diff --git a/frontend/module/icons/gitlab-icons.svg b/modules/gitlab_integration/frontend/module/icons/gitlab-icons.svg similarity index 100% rename from frontend/module/icons/gitlab-icons.svg rename to modules/gitlab_integration/frontend/module/icons/gitlab-icons.svg diff --git a/frontend/module/issue/issue.component.html b/modules/gitlab_integration/frontend/module/issue/issue.component.html similarity index 100% rename from frontend/module/issue/issue.component.html rename to modules/gitlab_integration/frontend/module/issue/issue.component.html diff --git a/frontend/module/issue/issue.component.sass b/modules/gitlab_integration/frontend/module/issue/issue.component.sass similarity index 100% rename from frontend/module/issue/issue.component.sass rename to modules/gitlab_integration/frontend/module/issue/issue.component.sass diff --git a/frontend/module/issue/issue.component.ts b/modules/gitlab_integration/frontend/module/issue/issue.component.ts similarity index 100% rename from frontend/module/issue/issue.component.ts rename to modules/gitlab_integration/frontend/module/issue/issue.component.ts diff --git a/frontend/module/main.ts b/modules/gitlab_integration/frontend/module/main.ts similarity index 100% rename from frontend/module/main.ts rename to modules/gitlab_integration/frontend/module/main.ts diff --git a/frontend/module/merge-request/merge-request.component.html b/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.html similarity index 100% rename from frontend/module/merge-request/merge-request.component.html rename to modules/gitlab_integration/frontend/module/merge-request/merge-request.component.html diff --git a/frontend/module/merge-request/merge-request.component.sass b/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.sass similarity index 100% rename from frontend/module/merge-request/merge-request.component.sass rename to modules/gitlab_integration/frontend/module/merge-request/merge-request.component.sass diff --git a/frontend/module/merge-request/merge-request.component.ts b/modules/gitlab_integration/frontend/module/merge-request/merge-request.component.ts similarity index 100% rename from frontend/module/merge-request/merge-request.component.ts rename to modules/gitlab_integration/frontend/module/merge-request/merge-request.component.ts diff --git a/frontend/module/merge-request/mr-pipeline.component.sass b/modules/gitlab_integration/frontend/module/merge-request/mr-pipeline.component.sass similarity index 100% rename from frontend/module/merge-request/mr-pipeline.component.sass rename to modules/gitlab_integration/frontend/module/merge-request/mr-pipeline.component.sass diff --git a/frontend/module/tab-header-issue/styles/tab-header-issue.sass b/modules/gitlab_integration/frontend/module/tab-header-issue/styles/tab-header-issue.sass similarity index 100% rename from frontend/module/tab-header-issue/styles/tab-header-issue.sass rename to modules/gitlab_integration/frontend/module/tab-header-issue/styles/tab-header-issue.sass diff --git a/frontend/module/tab-header-issue/tab-header-issue.component.ts b/modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.component.ts similarity index 100% rename from frontend/module/tab-header-issue/tab-header-issue.component.ts rename to modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.component.ts diff --git a/frontend/module/tab-header-issue/tab-header-issue.template.html b/modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.template.html similarity index 100% rename from frontend/module/tab-header-issue/tab-header-issue.template.html rename to modules/gitlab_integration/frontend/module/tab-header-issue/tab-header-issue.template.html diff --git a/frontend/module/tab-header-mr/styles/tab-header-mr.sass b/modules/gitlab_integration/frontend/module/tab-header-mr/styles/tab-header-mr.sass similarity index 100% rename from frontend/module/tab-header-mr/styles/tab-header-mr.sass rename to modules/gitlab_integration/frontend/module/tab-header-mr/styles/tab-header-mr.sass diff --git a/frontend/module/tab-header-mr/tab-header-mr.component.ts b/modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.component.ts similarity index 100% rename from frontend/module/tab-header-mr/tab-header-mr.component.ts rename to modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.component.ts diff --git a/frontend/module/tab-header-mr/tab-header-mr.template.html b/modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.template.html similarity index 100% rename from frontend/module/tab-header-mr/tab-header-mr.template.html rename to modules/gitlab_integration/frontend/module/tab-header-mr/tab-header-mr.template.html diff --git a/frontend/module/tab-issue/tab-issue.component.ts b/modules/gitlab_integration/frontend/module/tab-issue/tab-issue.component.ts similarity index 100% rename from frontend/module/tab-issue/tab-issue.component.ts rename to modules/gitlab_integration/frontend/module/tab-issue/tab-issue.component.ts diff --git a/frontend/module/tab-issue/tab-issue.template.html b/modules/gitlab_integration/frontend/module/tab-issue/tab-issue.template.html similarity index 100% rename from frontend/module/tab-issue/tab-issue.template.html rename to modules/gitlab_integration/frontend/module/tab-issue/tab-issue.template.html diff --git a/frontend/module/tab-issue/wp-gitlab-issue.service.ts b/modules/gitlab_integration/frontend/module/tab-issue/wp-gitlab-issue.service.ts similarity index 100% rename from frontend/module/tab-issue/wp-gitlab-issue.service.ts rename to modules/gitlab_integration/frontend/module/tab-issue/wp-gitlab-issue.service.ts diff --git a/frontend/module/tab-mrs/tab-mrs.component.ts b/modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.component.ts similarity index 100% rename from frontend/module/tab-mrs/tab-mrs.component.ts rename to modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.component.ts diff --git a/frontend/module/tab-mrs/tab-mrs.template.html b/modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.template.html similarity index 100% rename from frontend/module/tab-mrs/tab-mrs.template.html rename to modules/gitlab_integration/frontend/module/tab-mrs/tab-mrs.template.html diff --git a/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts b/modules/gitlab_integration/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts similarity index 100% rename from frontend/module/tab-mrs/wp-gitlab-mrs.service.ts rename to modules/gitlab_integration/frontend/module/tab-mrs/wp-gitlab-mrs.service.ts diff --git a/frontend/module/typings.d.ts b/modules/gitlab_integration/frontend/module/typings.d.ts similarity index 100% rename from frontend/module/typings.d.ts rename to modules/gitlab_integration/frontend/module/typings.d.ts diff --git a/lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb similarity index 100% rename from lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_issue_collection_representer.rb diff --git a/lib/api/v3/gitlab_issues/gitlab_issue_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_issue_representer.rb similarity index 100% rename from lib/api/v3/gitlab_issues/gitlab_issue_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_issue_representer.rb diff --git a/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb b/modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb similarity index 100% rename from lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_issues_by_work_package_api.rb diff --git a/lib/api/v3/gitlab_issues/gitlab_user_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_user_representer.rb similarity index 100% rename from lib/api/v3/gitlab_issues/gitlab_user_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_issues/gitlab_user_representer.rb diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb similarity index 100% rename from lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_collection_representer.rb diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb similarity index 100% rename from lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_merge_request_representer.rb diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb b/modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb similarity index 100% rename from lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_merge_requests_by_work_package_api.rb diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb similarity index 100% rename from lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_pipeline_representer.rb diff --git a/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb b/modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb similarity index 100% rename from lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb rename to modules/gitlab_integration/lib/api/v3/gitlab_merge_requests/gitlab_user_representer.rb diff --git a/lib/open_project/gitlab_integration.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration.rb similarity index 100% rename from lib/open_project/gitlab_integration.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration.rb diff --git a/lib/open_project/gitlab_integration/engine.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/engine.rb similarity index 100% rename from lib/open_project/gitlab_integration/engine.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/engine.rb diff --git a/lib/open_project/gitlab_integration/hook_handler.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/hook_handler.rb similarity index 100% rename from lib/open_project/gitlab_integration/hook_handler.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/hook_handler.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/helper.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/helper.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/helper.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/helper.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/issue_hook.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/issue_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/merge_request_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/note_hook.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/note_hook.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/note_hook.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/note_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/pipeline_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/push_hook.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/push_hook.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/push_hook.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/push_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handler/system_hook.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/system_hook.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handler/system_hook.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handler/system_hook.rb diff --git a/lib/open_project/gitlab_integration/notification_handlers.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handlers.rb similarity index 100% rename from lib/open_project/gitlab_integration/notification_handlers.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/notification_handlers.rb diff --git a/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb similarity index 100% rename from lib/open_project/gitlab_integration/patches/api/work_package_representer.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/patches/api/work_package_representer.rb diff --git a/lib/open_project/gitlab_integration/patches/work_package_patch.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/patches/work_package_patch.rb similarity index 100% rename from lib/open_project/gitlab_integration/patches/work_package_patch.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/patches/work_package_patch.rb diff --git a/lib/open_project/gitlab_integration/services.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services.rb similarity index 100% rename from lib/open_project/gitlab_integration/services.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services.rb diff --git a/lib/open_project/gitlab_integration/services/params_helper.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services/params_helper.rb similarity index 100% rename from lib/open_project/gitlab_integration/services/params_helper.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services/params_helper.rb diff --git a/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb similarity index 100% rename from lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_gitlab_user.rb diff --git a/lib/open_project/gitlab_integration/services/upsert_issue.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_issue.rb similarity index 100% rename from lib/open_project/gitlab_integration/services/upsert_issue.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_issue.rb diff --git a/lib/open_project/gitlab_integration/services/upsert_issue_note.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_issue_note.rb similarity index 100% rename from lib/open_project/gitlab_integration/services/upsert_issue_note.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_issue_note.rb diff --git a/lib/open_project/gitlab_integration/services/upsert_merge_request.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_merge_request.rb similarity index 100% rename from lib/open_project/gitlab_integration/services/upsert_merge_request.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_merge_request.rb diff --git a/lib/open_project/gitlab_integration/services/upsert_pipeline.rb b/modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_pipeline.rb similarity index 100% rename from lib/open_project/gitlab_integration/services/upsert_pipeline.rb rename to modules/gitlab_integration/lib/open_project/gitlab_integration/services/upsert_pipeline.rb diff --git a/lib/openproject-gitlab_integration.rb b/modules/gitlab_integration/lib/openproject-gitlab_integration.rb similarity index 100% rename from lib/openproject-gitlab_integration.rb rename to modules/gitlab_integration/lib/openproject-gitlab_integration.rb diff --git a/openproject-gitlab_integration.gemspec b/modules/gitlab_integration/openproject-gitlab_integration.gemspec similarity index 100% rename from openproject-gitlab_integration.gemspec rename to modules/gitlab_integration/openproject-gitlab_integration.gemspec