diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..162676cbef --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + +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. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + +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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + +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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + +14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + +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 AGPL, see +. diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index f3eb83374f..0000000000 --- a/LICENSE.md +++ /dev/null @@ -1,729 +0,0 @@ -# GNU Affero General Public License version 3 -License and Additional Terms for TWAKE software - -TWAKE is a secure, open-source, and enterprise-centric SaaS collaboration -platform, distributed under the GNU Affero GPL v3 License terms, with -Additional Terms pursuant to Section 7 of said license. - -These Additional Terms are not intended to be taken as a change of heart by -LINAGORA over the principles of free software and open source distribution, as -LINAGORA strongly believes in free software and open source distribution. -Free and open source software warrants an easy and reasonable access to software -innovation to large user communities, and LINAGORA is highly committed to supporting -free and open source software whenever possible. - -LINAGORA wishes its paternity over TWAKE software to be acknowledged, -regardless of its present or later use, modification, distribution and/or -evolutions. Accordingly, these terms aim at preserving LINAGORA moral rights -over TWAKE. - -We have taken care of not affecting product copying, improvements or deploying. -It is our conviction that the community will not be affected by these terms, -the ultimate goal of which is to ensure the sustainability of free and open -source software by supporting R&D and improving the visibility of LINAGORA as a -free and open source software publisher, while encouraging others to comply -with our common ideals. - -Pursuant to this license, you are therefore free to use the software and modify -it according to the GNU Affero General Public License version 3, provided that -you comply with its requirements, notably: - -- indicating, in a clear and unambiguous manner, that the software is a - modification of original code; -- retaining Appropriate Legal Notices in the source code and the user - interface; -- keeping any modifications of the software under the terms of the GNU Affero - General Public License version 3, including its Additional Terms pursuant to - its section 7, subsections (b), (c) and (e). - -Following are the applicable Additional Terms for use of the TWAKE software -pursuant to section 7, subsections (b), (c) and (e) of the GNU Affero General -Public License version 3. - -### Additional Terms applicable for TWAKE software - -The following additional terms are applicable to the use, modification and -distribution of TWAKE software: - -**1. Notices and Attribution:** - -The interactive user interfaces in modified source and object code versions of -this program must display Appropriate Legal Notices, as required under Section -5 of the GNU Affero General Public License version 3. - -In accordance with Section 7 and subsection (b) of the GNU Affero General -Public License version 3, these Appropriate Legal Notices consist in the -display, in a clearly legible manner, of the Notice “Twake is powered by -Linagora.” in the TWAKE user interface as well as for any and all type of -outbound messages (e.g. e-mail and meeting requests). - -Retaining this Notice in any and all free and Open Source versions of TWAKE -is mandatory notwithstanding any other terms and conditions. - -If the display of the Appropriate Legal Notices, and/or logos is not reasonably -feasible for technical reasons, the Appropriate Legal Notices must display the -words "Twake is powered by Linagora." - -These Notices can be freely translated and replaced by any notice of strictly -identical meaning in another language according to localization of the software, -provided such notice clearly displays the words “TWAKE” and “LINAGORA”. - -Regardless of the notice language, the Logo/words "TWAKE" must be a clickable -hypertext link that leads directly to the Internet URL https://twake.app. -The Logo/word "LINAGORA" must be a clickable hypertext link that leads directly -to the Internet URL https://linagora.com. - -**2. Use of the TWAKE and LINAGORA trademarks and logos** - -TWAKE™ and LINAGORA™ are registered trademarks of LINAGORA. - -Pursuant to Section 7, subsections (c) and (e) of the GNU Affero General Public -License version 3, this license allows limited use of these trademarks under -the following terms: - -All LINAGORA trademarks, including TWAKE™ and LINAGORA™ logos shall be used by -the licensees and sublicensees for the sole purpose of complying with the -present Additional Terms to the GNU Affero General Public License version 3, -excluding any other purpose without written consent obtained from LINAGORA. - -Using these trademarks without the (TM) trademark notice symbol, removing these -trademarks from the software, modifying these trademarks in any manner except -proportional scaling (under the proviso that such scaling keeps the trademark -clearly legible), or using these trademarks to promote any products or services -commercially, or on product packaging, websites, books, documentation or any -other publication without a written, signed agreement with LINAGORA is strictly -prohibited, and constitutes an infringement of LINAGORA intellectual property -rights over these trademarks. - -Using these trademarks in a way harmful,damaging or detrimental to the value of -the TWAKE brand or any other LINAGORA trademarks, integrity, image, reputation, -and/or goodwill, as determined by LINAGORA, is also strictly prohibited, and -constitutes an infringement of LINAGORA intellectual property rights over these -trademarks as well. - -**Please report any possible violation of the GNU Affero General Public License -version 3, any violation of the hereabove Additional Terms, any infringement -and/or misuse of any TWAKE or LINAGORA trade marks and/or a violation of the -aforementioned Trademark Policy at .** - - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - -Copyright © 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 Affero General Public License is a free, copyleft license for software -and other kinds of works, specifically designed to ensure cooperation with the -community in the case of network server software. - -The licenses for most software and other practical works are designed to take -away your freedom to share and change the works. By contrast, our General -Public Licenses are 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. - -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. - -Developers that use our General Public Licenses protect your rights with two -steps: (1) assert copyright on the software, and (2) offer you this License -which gives you legal permission to copy, distribute and/or modify the -software. - -A secondary benefit of defending all users' freedom is that improvements made -in alternate versions of the program, if they receive widespread use, become -available for other developers to incorporate. Many developers of free software -are heartened and encouraged by the resulting cooperation. However, in the case -of software used on network servers, this result may fail to come about. The -GNU General Public License permits making a modified version and letting the -public access it on a server without ever releasing its source code to the -public. - -The GNU Affero General Public License is designed specifically to ensure that, -in such cases, the modified source code becomes available to the community. It -requires the operator of a network server to provide the source code of the -modified version running there to the users of that server. Therefore, public -use of a modified version, on a publicly accessible server, gives the public -access to the source code of the modified version. - -An older license, called the Affero General Public License and published by -Affero, was designed to accomplish similar goals. This is a different license, -not a version of the Affero GPL, but Affero has released a new version of the -Affero GPL which permits relicensing under this license. - -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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. - -Notwithstanding any other provision of this License, if you modify the Program, -your modified version must prominently offer all users interacting with it -remotely through a computer network (if your version supports such interaction) -an opportunity to receive the Corresponding Source of your version by providing -access to the Corresponding Source from a network server at no charge, through -some standard or customary means of facilitating copying of software. This -Corresponding Source shall include the Corresponding Source for any work -covered by version 3 of the GNU General Public License that is incorporated -pursuant to the following paragraph. - -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 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 work with which it is combined will remain -governed by version 3 of the GNU General Public License. 14. Revised Versions -of this License. - -The Free Software Foundation may publish revised and/or new versions of the GNU -Affero 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 Affero 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 Affero 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 Affero 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 - -In order to distribute a program under the GNU Affero General Public License -version 3 with the Additional Terms for TWAKE software, 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 Affero General Public License as published by the Free Software -Foundation, either version 3 of the License, or (at your option) any later version, -provided you comply with the Additional Terms applicable for TWAKE software by -LINAGORA pursuant to Section 7 of the GNU Affero General Public License, subsections -(b), (c), and (e), pursuant to which you must notably (i) retain the “Twake is powered -by Linagora” notice appended to any type of outbound messages (e.g. e-mail and meeting -requests) as well as in the TWAKE user interface, (ii) retain all hypertext links between -TWAKE and https://twake.app, as well as between LINAGORA and LINAGORA.com, and (iii) refrain -from infringing LINAGORA intellectual property rights over its trademarks and commercial brands. -Other Additional Terms apply, see for more details.` - -`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 -Affero General Public License for more details.` - -`You should have received a copy of the GNU Affero General Public License and its applicable Additional -Terms for TWAKE along with this program. If not, see for the GNU Affero -General Public License version 3 and for the Additional Terms -applicable to the TWAKE software.` - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, you should also make sure -that it provides a way for users to get its source. For example, if your program is a web application, -its interface could display a "Source" link that leads users to an archive of the code. There are many ways -you could offer source, and different solutions will be better for different programs; see section 13 for -the specific requirements. - -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 AGPL, see . - diff --git a/README.md b/README.md index 6d3d5223d7..895c047986 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Twake offers all the features for collaboration : - Video call and conferencing - Real time document collaboration - + ## Demo @@ -30,9 +30,10 @@ You can try Twake in SaaS. Or run your own local Twake instance with : -``` -cd twake -sudo ./start.sh +```bash +cd twake +export COMPOSE_FILE=docker-compose.onpremise.yml +docker compose up -d ``` Twake will be running on http//localhost and by default redirect to https and uses a self-signed certificate. If you want to run http only then set SSL_CERTS=none at docker-compose.yml diff --git a/changelog.md b/changelog.md index 86cd2d63a6..2548eb86ea 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,4 @@ -# Twake 2022.Q3.1120 +# Twake 2022.Q4.1120 - Message delivery status 🟢 - New quote-reply feature ⤴️ diff --git a/twake/backend/node/src/core/platform/services/knowledge-graph/index.ts b/twake/backend/node/src/core/platform/services/knowledge-graph/index.ts index 73b2976213..3a10e451d7 100644 --- a/twake/backend/node/src/core/platform/services/knowledge-graph/index.ts +++ b/twake/backend/node/src/core/platform/services/knowledge-graph/index.ts @@ -103,9 +103,9 @@ export default class KnowledgeGraphService if ( this.kgAPIClient && - (await this.shouldForwardEvent(data.resource.cache.companies, data.resource.id)) + (await this.shouldForwardEvent(data.resource.cache?.companies || [], data.resource.id)) ) { - for (const companyId of data.resource.cache.companies) { + for (const companyId of data.resource.cache?.companies || []) { this.kgAPIClient.onUserCreated(companyId, data.resource); } } diff --git a/twake/backend/node/src/services/files/services/index.ts b/twake/backend/node/src/services/files/services/index.ts index c6548d310d..ffd2e406c7 100644 --- a/twake/backend/node/src/services/files/services/index.ts +++ b/twake/backend/node/src/services/files/services/index.ts @@ -182,6 +182,11 @@ export class FileServiceImpl { return entity; } + async exists(id: string, companyId: string, context?: CompanyExecutionContext): Promise { + const entity = await this.getFile({ id, company_id: companyId }, context); + return !!entity; + } + async download( id: string, context: CompanyExecutionContext, @@ -242,7 +247,7 @@ export class FileServiceImpl { return this.getFile({ id, company_id: context.company.id }, context); } - async getFile(pk: Pick, context: ExecutionContext): Promise { + async getFile(pk: Pick, context?: ExecutionContext): Promise { return this.repository.findOne(pk, {}, context); } diff --git a/twake/backend/node/src/services/messages/services/views.ts b/twake/backend/node/src/services/messages/services/views.ts index e01598f505..55be1416bb 100644 --- a/twake/backend/node/src/services/messages/services/views.ts +++ b/twake/backend/node/src/services/messages/services/views.ts @@ -319,31 +319,29 @@ export class ViewsServiceImpl implements TwakeServiceProvider, Initializable { options: SearchMessageFilesOptions, context?: ExecutionContext, ): Promise> { - return await this.searchFilesRepository - .search( - { - ...(options.isFile ? { is_file: true } : {}), - ...(options.isMedia ? { is_media: true } : {}), + const temp = await this.searchFilesRepository.search( + { + ...(options.isFile ? { is_file: true } : {}), + ...(options.isMedia ? { is_media: true } : {}), + }, + { + pagination, + ...(options.companyId ? { $in: [["cache_company_id", [options.companyId]]] } : {}), + ...(options.workspaceId ? { $in: [["cache_workspace_id", [options.workspaceId]]] } : {}), + ...(options.channelId ? { $in: [["cache_channel_id", [options.channelId]]] } : {}), + ...(options.sender ? { $in: [["cache_user_id", [options.sender]]] } : {}), + ...(options.extension ? { $in: [["extension", [options.extension]]] } : {}), + $text: { + $search: options.search, }, - { - pagination, - ...(options.companyId ? { $in: [["cache_company_id", [options.companyId]]] } : {}), - ...(options.workspaceId ? { $in: [["cache_workspace_id", [options.workspaceId]]] } : {}), - ...(options.channelId ? { $in: [["cache_channel_id", [options.channelId]]] } : {}), - ...(options.sender ? { $in: [["cache_user_id", [options.sender]]] } : {}), - ...(options.extension ? { $in: [["extension", [options.extension]]] } : {}), - $text: { - $search: options.search, - }, - $sort: { - created_at: "desc", - }, + $sort: { + created_at: "desc", }, - context, - ) - .then(a => { - return a; - }); + }, + context, + ); + + return new ListResult(temp.type, await this.checkFiles(temp.getEntities()), temp.nextPage); } async listUserMarkedFiles( @@ -394,7 +392,7 @@ export class ViewsServiceImpl implements TwakeServiceProvider, Initializable { const messageFilePromises: Promise[] = refs.map( async ref => { try { - return { + const res = { ...(await this.repositoryMessageFile.findOne( { message_id: ref.message_id, @@ -405,6 +403,8 @@ export class ViewsServiceImpl implements TwakeServiceProvider, Initializable { )), context: ref, }; + + return res; } catch (e) { return null; } @@ -442,8 +442,19 @@ export class ViewsServiceImpl implements TwakeServiceProvider, Initializable { return new ListResult( "file", - fileWithUserAndMessage, + await this.checkFiles(fileWithUserAndMessage), nextPageUploads || nextPageDownloads, ); } + + async checkFiles(files: T[]): Promise { + const results = await Promise.all( + files.map(async file => { + if (file.metadata.source !== "internal") return true; + const ei = file.metadata.external_id; + return await gr.services.files.exists(ei.id, ei.company_id); + }), + ); + return files.filter((_v, index) => results[index]); + } } diff --git a/twake/backend/node/src/services/messages/web/controllers/messages.ts b/twake/backend/node/src/services/messages/web/controllers/messages.ts index 3f92a4ea71..b29b3a1075 100644 --- a/twake/backend/node/src/services/messages/web/controllers/messages.ts +++ b/twake/backend/node/src/services/messages/web/controllers/messages.ts @@ -14,7 +14,7 @@ import { ThreadExecutionContext, } from "../../types"; import { handleError } from "../../../../utils/handleError"; -import { Pagination } from "../../../../core/platform/framework/api/crud-service"; +import { CrudException, Pagination } from "../../../../core/platform/framework/api/crud-service"; import { getThreadMessageWebsocketRoom } from "../realtime"; import { ThreadPrimaryKey } from "../../entities/threads"; import { extendExecutionContentWithChannel } from "./index"; @@ -74,6 +74,36 @@ export class MessagesController throw "Message must be in a thread"; } + let hasOneMembership = false; + for (const participant of thread.participants) { + if (thread.created_by === context.user.id) { + hasOneMembership = true; + break; + } + if (participant.type === "channel") { + const isMember = await gr.services.channels.members.getChannelMember( + { id: context.user.id }, + { + company_id: participant.company_id, + workspace_id: participant.workspace_id, + id: participant.id, + }, + ); + if (isMember) { + hasOneMembership = true; + break; + } + } else if (participant.type === "user") { + if (participant.id === context.user.id) { + hasOneMembership = true; + break; + } + } + } + if (!hasOneMembership) { + throw CrudException.notFound("You can't post in this thread"); + } + const result = await gr.services.messages.messages.save( { id: request.params.message_id || undefined, diff --git a/twake/backend/node/src/services/messages/web/controllers/threads.ts b/twake/backend/node/src/services/messages/web/controllers/threads.ts index cd66e02770..495179b1d1 100644 --- a/twake/backend/node/src/services/messages/web/controllers/threads.ts +++ b/twake/backend/node/src/services/messages/web/controllers/threads.ts @@ -10,6 +10,7 @@ import { handleError } from "../../../../utils/handleError"; import { CompanyExecutionContext } from "../../types"; import { ParticipantObject, Thread } from "../../entities/threads"; import gr from "../../../global-resolver"; +import { CrudException } from "../../../../core/platform/framework/api/crud-service"; export class ThreadsController implements @@ -39,6 +40,27 @@ export class ThreadsController reply: FastifyReply, ): Promise> { const context = getCompanyExecutionContext(request); + + const participants = + (request.body.resource.participants?.length + ? request.body.resource?.participants + : request.body.options?.participants?.add) || []; + for (const participant of participants) { + if (participant.type === "channel") { + const isMember = await gr.services.channels.members.getChannelMember( + { id: context.user.id }, + { + company_id: participant.company_id, + workspace_id: participant.workspace_id, + id: participant.id, + }, + ); + if (!isMember) { + throw CrudException.notFound("Channel not found"); + } + } + } + try { const result = await gr.services.messages.threads.save( { diff --git a/twake/backend/node/src/services/messages/web/controllers/views.ts b/twake/backend/node/src/services/messages/web/controllers/views.ts index de1f641e9f..4cd20aacac 100644 --- a/twake/backend/node/src/services/messages/web/controllers/views.ts +++ b/twake/backend/node/src/services/messages/web/controllers/views.ts @@ -2,7 +2,11 @@ import { FastifyReply, FastifyRequest } from "fastify"; import { ResourceListResponse } from "../../../../utils/types"; import { Message } from "../../entities/messages"; import { handleError } from "../../../../utils/handleError"; -import { ListResult, Pagination } from "../../../../core/platform/framework/api/crud-service"; +import { + CrudException, + ListResult, + Pagination, +} from "../../../../core/platform/framework/api/crud-service"; import { ChannelViewExecutionContext, FlatFileFromMessage, @@ -39,6 +43,18 @@ export class ViewsController { const query = { ...request.query, include_users: request.query.include_users }; const context = getChannelViewExecutionContext(request); + const isMember = await gr.services.channels.members.getChannelMember( + { id: context.user.id }, + { + company_id: request.params.company_id, + workspace_id: request.params.workspace_id, + id: request.params.channel_id, + }, + ); + if (!isMember) { + throw CrudException.notFound("Channel not found"); + } + let resources: ListResult; try { diff --git a/twake/backend/node/src/services/notifications/services/preferences.ts b/twake/backend/node/src/services/notifications/services/preferences.ts index fa2c8bdae1..6c61c40061 100644 --- a/twake/backend/node/src/services/notifications/services/preferences.ts +++ b/twake/backend/node/src/services/notifications/services/preferences.ts @@ -39,7 +39,25 @@ export class NotificationPreferencesService implements TwakeServiceProvider, Ini const notificationPreferences = (user?.preferences?.notifications || []).find(n => { return n.company_id === pk.company_id && n.workspace_id === pk.workspace_id; }); - return notificationPreferences; + return ( + notificationPreferences || { + company_id: pk.company_id, + workspace_id: pk.workspace_id, + preferences: { + highlight_words: [], + night_break: { + enable: false, + from: 0, + to: 0, + }, + private_message_content: false, + mobile_notifications: "always", + email_notifications_delay: 15, + deactivate_notifications_until: 0, + notification_sound: "default", + }, + } + ); } /** We can define preferences for specifically a workspace or for all a company or all Twake diff --git a/twake/backend/node/src/services/online/service/index.ts b/twake/backend/node/src/services/online/service/index.ts index 06d8b7fcc1..6c71bfe554 100644 --- a/twake/backend/node/src/services/online/service/index.ts +++ b/twake/backend/node/src/services/online/service/index.ts @@ -60,7 +60,7 @@ export default class OnlineServiceImpl implements TwakeServiceProvider { this.logger.debug(`Got an online:set request for ${(request.data || []).length} users`); this.broadcastOnline(event, companies); - this.setLastSeenOnline([event.user.id], Date.now(), false); + this.setLastSeenOnline([event.user.id], Date.now(), true); ack(); }, ); diff --git a/twake/backend/node/src/services/workspaces/web/controllers/workspace-users.ts b/twake/backend/node/src/services/workspaces/web/controllers/workspace-users.ts index 7aed983019..3d5a7f84dc 100644 --- a/twake/backend/node/src/services/workspaces/web/controllers/workspace-users.ts +++ b/twake/backend/node/src/services/workspaces/web/controllers/workspace-users.ts @@ -84,7 +84,7 @@ export class WorkspaceUsersCrudController status: user.status_icon, last_activity: user.last_activity, cache: { - companies: user.cache.companies, + companies: user.cache?.companies || [], }, companies: userCompanies .filter(cu => companiesMap.get(cu.group_id)) diff --git a/twake/backend/node/test/e2e/messages/messages.messages.spec.ts b/twake/backend/node/test/e2e/messages/messages.messages.spec.ts index 0eec53a0de..2d30b256c1 100644 --- a/twake/backend/node/test/e2e/messages/messages.messages.spec.ts +++ b/twake/backend/node/test/e2e/messages/messages.messages.spec.ts @@ -9,7 +9,13 @@ import { } from "../../../src/utils/types"; import { deserialize } from "class-transformer"; import { ParticipantObject, Thread } from "../../../src/services/messages/entities/threads"; -import { createMessage, e2e_createMessage, e2e_createThread } from "./utils"; +import { + createMessage, + createParticipant, + e2e_createChannel, + e2e_createMessage, + e2e_createThread, +} from "./utils"; import { Message } from "../../../src/services/messages/entities/messages"; import { v1 as uuidv1 } from "uuid"; import { MessageWithReplies } from "../../../src/services/messages/types"; @@ -98,98 +104,10 @@ describe("The Messages feature", () => { expect(listResponse.statusCode).toBe(200); expect(listResult.resources.length).toBe(3); }); - - it("should move the message in threads", async () => { - const userA = uuidv1(); - const userB = uuidv1(); - const userC = uuidv1(); - - const thread1Request = await e2e_createThread( - platform, - [], - createMessage({ text: "Message A in thread 1", user_id: userA }), - ); - const thread1Result: ResourceUpdateResponse = deserialize( - ResourceUpdateResponse, - thread1Request.body, - ); - const thread1: Thread = thread1Result.resource; - - const thread2Request = await e2e_createThread( - platform, - [], - createMessage({ text: "Message B in thread 2", user_id: userB }), - ); - const thread2Result: ResourceUpdateResponse = deserialize( - ResourceUpdateResponse, - thread2Request.body, - ); - const thread2: Thread = thread2Result.resource; - - const messageCRequest = await e2e_createMessage( - platform, - thread1.id, - createMessage({ text: "Message C in thread 1", user_id: userC }), - ); - const messageCResult: ResourceUpdateResponse = deserialize( - ResourceUpdateResponse, - messageCRequest.body, - ); - const messageC: Message = messageCResult.resource; - - const messageCAfterMoveRequest = await platform.app.inject({ - method: "POST", - url: `${url}/companies/${platform.workspace.company_id}/threads/${thread2.id}/messages/${messageC.id}`, - headers: { - authorization: `Bearer ${await platform.auth.getJWTToken({ sub: userA })}`, - }, - payload: { - resource: messageC, - options: { - previous_thread: thread1.id, - }, - }, - }); - const messageCAfterMoveResult: ResourceUpdateResponse = deserialize( - ResourceUpdateResponse, - messageCAfterMoveRequest.body, - ); - const messageCAfterMove: Message = messageCAfterMoveResult.resource; - - //See if message was moved correctly to the new thread - expect(messageCAfterMove.user_id).toBe(userC); - expect(messageCAfterMove.thread_id).toBe(thread2.id); - - const messageCAfterMove2Request = await platform.app.inject({ - method: "POST", - url: `${url}/companies/${platform.workspace.company_id}/threads/${messageC.id}/messages/${messageC.id}`, - headers: { - authorization: `Bearer ${await platform.auth.getJWTToken({ sub: userA })}`, - }, - payload: { - resource: messageC, - options: { - previous_thread: thread2.id, - }, - }, - }); - const messageCAfterMove2Result: ResourceUpdateResponse = deserialize( - ResourceUpdateResponse, - messageCAfterMove2Request.body, - ); - const messageCAfter2Move: Message = messageCAfterMove2Result.resource; - - //See if message was moved correctly to new standalone thread - expect(messageCAfter2Move.user_id).toBe(userC); - expect(messageCAfter2Move.thread_id).toBe(messageCAfter2Move.id); - - //TODO check counts - }); }); describe("Inbox", () => { it("Should get recent user messages", async done => { - const channel = channelUtils.getChannel(); const directChannelIn = channelUtils.getDirectChannel(); const members = [platform.currentUser.id, uuidv1()]; const directWorkspace: Workspace = { @@ -198,7 +116,6 @@ describe("The Messages feature", () => { }; await Promise.all([ - gr.services.channels.channels.save(channel, {}, getContext()), gr.services.channels.channels.save( directChannelIn, { @@ -209,12 +126,12 @@ describe("The Messages feature", () => { ]); const recipient: ParticipantObject = { - company_id: platform.workspace.company_id, created_at: 0, created_by: "", - id: channel.id, type: "channel", + company_id: directChannelIn.company_id, workspace_id: "direct", + id: directChannelIn.id, }; for (let i = 0; i < 6; i++) { @@ -270,8 +187,8 @@ describe("The Messages feature", () => { application_id: null, text: expect.any(String), cache: { - company_id: channel.company_id, - channel_id: channel.id, + company_id: directChannelIn.company_id, + channel_id: directChannelIn.id, }, }); } diff --git a/twake/backend/node/test/e2e/messages/messages.search.spec.ts b/twake/backend/node/test/e2e/messages/messages.search.spec.ts index 406300eab0..f1c3afdef2 100644 --- a/twake/backend/node/test/e2e/messages/messages.search.spec.ts +++ b/twake/backend/node/test/e2e/messages/messages.search.spec.ts @@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, it } from "@jest/globals"; import { init, TestPlatform } from "../setup"; import { TestDbService } from "../utils.prepare.db"; import { v1 as uuidv1 } from "uuid"; -import { createMessage, e2e_createMessage, e2e_createThread } from "./utils"; +import { createMessage, e2e_createChannel, e2e_createMessage, e2e_createThread } from "./utils"; import { ResourceUpdateResponse } from "../../../src/utils/types"; import { ParticipantObject, Thread } from "../../../src/services/messages/entities/threads"; import { deserialize } from "class-transformer"; @@ -121,22 +121,22 @@ describe("The /messages API", () => { done(); }); it("Filter out messages from channels we are not member of", async done => { - const channel = await createChannel(); - const anotherChannel = await createChannel(uuidv1()); const anotherUserId = uuidv1(); + const channel = await e2e_createChannel(platform, [platform.currentUser.id, anotherUserId]); + const anotherChannel = await e2e_createChannel(platform, [anotherUserId], anotherUserId); //Test user is not the owner const participant = { type: "channel", - id: channel.id, - company_id: platform.workspace.company_id, - workspace_id: platform.workspace.workspace_id, + id: channel.resource.id, + company_id: channel.resource.company_id, + workspace_id: channel.resource.workspace_id, } as ParticipantObject; const participant2 = { type: "channel", - id: anotherChannel.id, - company_id: platform.workspace.company_id, - workspace_id: platform.workspace.workspace_id, + id: anotherChannel.resource.id, + company_id: anotherChannel.resource.company_id, + workspace_id: anotherChannel.resource.workspace_id, } as ParticipantObject; const file = new MessageFile(); @@ -148,7 +148,7 @@ describe("The /messages API", () => { await createReply(firstThreadId, "Filtered message 1-3"); await createReply(firstThreadId, "Filtered message 1-4", { files: [file] }); - const secondThreadId = await createThread("Filtered thread 2", [participant2]); + const secondThreadId = await createThread("Filtered thread 2", [participant2], anotherUserId); await createReply(secondThreadId, "Filtered message 2-1"); await createReply(secondThreadId, "Filtered message 2-2"); await createReply(secondThreadId, "Filtered message 2-3"); @@ -204,8 +204,13 @@ describe("The /messages API", () => { return creationResult.entity; } - async function createThread(text, participants: ParticipantObject[]) { - const response = await e2e_createThread(platform, participants, createMessage({ text: text })); + async function createThread(text, participants: ParticipantObject[], owner?: string) { + const response = await e2e_createThread( + platform, + participants, + createMessage({ text: text, user_id: owner }), + owner, + ); const result: ResourceUpdateResponse = deserialize( ResourceUpdateResponse, diff --git a/twake/backend/node/test/e2e/messages/messages.views.channel.spec.ts b/twake/backend/node/test/e2e/messages/messages.views.channel.spec.ts index 615db91d8f..14b0029870 100644 --- a/twake/backend/node/test/e2e/messages/messages.views.channel.spec.ts +++ b/twake/backend/node/test/e2e/messages/messages.views.channel.spec.ts @@ -5,7 +5,13 @@ import { ResourceListResponse, ResourceUpdateResponse } from "../../../src/utils import { deserialize } from "class-transformer"; import { v4 as uuidv4 } from "uuid"; import { Thread } from "../../../src/services/messages/entities/threads"; -import { createMessage, createParticipant, e2e_createMessage, e2e_createThread } from "./utils"; +import { + createMessage, + createParticipant, + e2e_createChannel, + e2e_createMessage, + e2e_createThread, +} from "./utils"; import { MessageWithReplies } from "../../../src/services/messages/types"; describe("The Messages feature", () => { @@ -42,7 +48,7 @@ describe("The Messages feature", () => { describe("On user use messages in channel view", () => { it("should create a message and retrieve it in channel view", async () => { - const channelId = uuidv4(); + const channel = await e2e_createChannel(platform, [platform.currentUser.id]); const response = await e2e_createThread( platform, @@ -50,7 +56,9 @@ describe("The Messages feature", () => { createParticipant( { type: "channel", - id: channelId, + id: channel.resource.id, + workspace_id: channel.resource.workspace_id, + company_id: channel.resource.company_id, }, platform, ), @@ -73,7 +81,9 @@ describe("The Messages feature", () => { createParticipant( { type: "channel", - id: channelId, + id: channel.resource.id, + workspace_id: channel.resource.workspace_id, + company_id: channel.resource.company_id, }, platform, ), @@ -89,7 +99,9 @@ describe("The Messages feature", () => { createParticipant( { type: "channel", - id: channelId, + id: channel.resource.id, + workspace_id: channel.resource.workspace_id, + company_id: channel.resource.company_id, }, platform, ), @@ -100,7 +112,7 @@ describe("The Messages feature", () => { const jwtToken = await platform.auth.getJWTToken(); const listResponse = await platform.app.inject({ method: "GET", - url: `${url}/companies/${platform.workspace.company_id}/workspaces/${platform.workspace.workspace_id}/channels/${channelId}/feed?replies_per_thread=3&include_users=1`, + url: `${url}/companies/${channel.resource.company_id}/workspaces/${channel.resource.workspace_id}/channels/${channel.resource.id}/feed?replies_per_thread=3&include_users=1`, headers: { authorization: `Bearer ${jwtToken}`, }, diff --git a/twake/backend/node/test/e2e/messages/utils.ts b/twake/backend/node/test/e2e/messages/utils.ts index 6b813ca506..6a5380ec91 100644 --- a/twake/backend/node/test/e2e/messages/utils.ts +++ b/twake/backend/node/test/e2e/messages/utils.ts @@ -1,19 +1,50 @@ -import { v1 } from "uuid"; +import { deserialize } from "class-transformer"; import { getInstance as getMessageInstance, Message, } from "../../../src/services/messages/entities/messages"; import { ParticipantObject } from "../../../src/services/messages/entities/threads"; +import { Channel, ResourceCreateResponse } from "../../../src/utils/types"; import { TestPlatform } from "../setup"; const url = "/internal/services/messages/v1"; +export const e2e_createChannel = async ( + platform: TestPlatform, + members: string[], + owner?: string, +) => { + const jwtToken = await platform.auth.getJWTToken({ sub: owner || platform.currentUser.id }); + const response = await platform.app.inject({ + method: "POST", + url: `/internal/services/channels/v1/companies/${platform.workspace.company_id}/workspaces/direct/channels`, + headers: { + authorization: `Bearer ${jwtToken}`, + }, + payload: { + options: { + members, + }, + resource: { + description: "A direct channel description", + visibility: "direct", + }, + }, + }); + const channelCreateResult: ResourceCreateResponse = deserialize( + ResourceCreateResponse, + response.body, + ); + return channelCreateResult; +}; + export const e2e_createThread = async ( platform: TestPlatform, participants: ParticipantObject[], message: Message, + owner?: string, ) => { - const jwtToken = await platform.auth.getJWTToken(); + const jwtToken = await platform.auth.getJWTToken({ sub: owner || platform.currentUser.id }); const res = await platform.app.inject({ method: "POST", url: `${url}/companies/${platform.workspace.company_id}/threads`, diff --git a/twake/docker-compose.onpremise.yml b/twake/docker-compose.onpremise.yml index a85cfb0d4c..2e9d0aa35b 100644 --- a/twake/docker-compose.onpremise.yml +++ b/twake/docker-compose.onpremise.yml @@ -8,43 +8,59 @@ services: - 9042:9042 volumes: - ./docker-data/scylladb:/var/lib/scylla + healthcheck: + test: ["CMD-SHELL", "nodetool status | grep UN"] + interval: 30s + timeout: 10s + retries: 3 rabbitmq: image: rabbitmq:3 + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 10s + retries: 3 node: - image: twaketech/twake-node:2021.Q2.505 + build: + context: . + dockerfile: docker/twake-node/Dockerfile environment: - NODE_ENV=production - PUBSUB_URLS=amqp://guest:guest@rabbitmq:5672 volumes: - - ./configuration/backend-node/production.json:/usr/src/app/config/production.json + - ./default-configuration/backend-node/production.json:/usr/src/app/config/production.json - ./docker-data/documents/:/storage/ depends_on: - - scylladb - - rabbitmq - links: - - scylladb + scylladb: + condition: service_healthy + rabbitmq: + condition: service_healthy php: - image: twaketech/twake-php:2021.Q2.505 + build: + context: . + dockerfile: docker/twake-php/Dockerfile environment: - DEV=production volumes: - - ./configuration/backend/Parameters.php:/configuration/Parameters.php + - ./default-configuration/backend/Parameters.php:/configuration/Parameters.php - ./connectors/:/twake-core/src/BuiltInConnectors/Connectors - ./docker-data/drive/:/twake-core/drive/ - ./docker-data/fpm/:/etc/docker-data/fpm/ - ./docker-data/drive-preview/:/twake-core/web/medias/ - ./docker-data/uploads/:/twake-core/web/upload/ depends_on: - - scylladb - - rabbitmq - links: - - scylladb + scylladb: + condition: service_healthy + rabbitmq: + condition: service_healthy nginx: - image: twaketech/twake-nginx:2021.Q2.505 + build: + context: . + dockerfile: docker/twake-nginx/Dockerfile environment: - DEV=production - SSL_CERTS=selfsigned diff --git a/twake/docker/twake-nginx/Dockerfile b/twake/docker/twake-nginx/Dockerfile old mode 100644 new mode 100755 index cc4fd30e40..144df56c9e --- a/twake/docker/twake-nginx/Dockerfile +++ b/twake/docker/twake-nginx/Dockerfile @@ -1,66 +1,31 @@ -FROM debian:11 +# First stage: building frontend +FROM node:lts AS build -RUN apt-get update && apt-get -y install cron - -RUN apt-get update && apt-get install -y \ - nginx - -RUN apt-get update && apt-get install -y wget - -RUN rm /etc/nginx/sites-enabled/default - -RUN usermod -u 1000 www-data - -RUN apt-get remove certbot -RUN apt-get install -y python3 python3-venv libaugeas0 -RUN python3 -m venv /opt/certbot/ -RUN /opt/certbot/bin/pip install --upgrade pip -RUN /opt/certbot/bin/pip install certbot -RUN ln -s /opt/certbot/bin/certbot /usr/bin/certbot - -ADD docker/twake-nginx/nginx.conf /etc/nginx/nginx.conf - -# Install yarn -RUN apt-get update -RUN apt-get -y install curl -RUN apt-get -y install apt-transport-https ca-certificates apt-utils gnupg -RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - -RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list -RUN apt-get update -RUN apt-get -y install yarn -RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - -RUN apt-get install -y nodejs -RUN yarn global add webpack -RUN yarn global add webpack-cli -RUN apt-get update -RUN apt-get -y install git - -# Install frontend +RUN yarn global add webpack webpack-cli +COPY frontend /twake-react/ WORKDIR /twake-react/ -ADD docker/twake-nginx/site.conf /etc/nginx/sites-available/site.template -ADD docker/twake-nginx/redirect.conf /etc/nginx/sites-available/redirect -ADD docker/twake-nginx-php-only/nginx.conf /etc/nginx/nginx.conf -RUN apt-get update && apt-get install gettext-base -RUN echo "upstream php-upstream { server php:9000; }" > /etc/nginx/conf.d/upstream.conf - -COPY frontend /twake-react/ -RUN cp /twake-react/src/app/environment/environment.ts.dist /twake-react/src/app/environment/environment.ts -RUN yarn install --network-timeout 1000000000 -ENV GENERATE_SOURCEMAP false -RUN cat /twake-react/src/app/environment/environment.ts.dist -RUN cat /twake-react/src/app/environment/environment.ts ENV GENERATE_SOURCEMAP=false -RUN yarn build -RUN rm -R node_modules - -RUN cp /twake-react/src/app/environment/environment.ts.dist /environment.ts.dist - +RUN cp /twake-react/src/app/environment/environment.ts.dist /twake-react/src/app/environment/environment.ts && \ + yarn install --network-timeout 1000000000 && \ + # cat /twake-react/src/app/environment/environment.ts.dist && \ + # cat /twake-react/src/app/environment/environment.ts && \ + yarn build && \ + rm -rf node_modules + +# Second stage: configuring nginx and copying build artifacts +FROM nginx:latest +COPY docker/twake-nginx/nginx.conf /etc/nginx/nginx.conf +COPY docker/twake-nginx/site.conf /etc/nginx/sites-available/site.template +COPY docker/twake-nginx/redirect.conf /etc/nginx/sites-available/redirect COPY docker/twake-nginx/entrypoint.sh / COPY docker/twake-nginx/self-signed.sh /usr/local/bin/ -RUN chmod 0777 /entrypoint.sh -ENTRYPOINT /entrypoint.sh "$DEV" +COPY --from=build /twake-react /twake-react + +RUN chmod +x /entrypoint.sh && rm /etc/nginx/conf.d/default.conf + +ENTRYPOINT ["/entrypoint.sh", "$DEV"] EXPOSE 80 EXPOSE 443 diff --git a/twake/docker/twake-nginx/entrypoint.sh b/twake/docker/twake-nginx/entrypoint.sh old mode 100644 new mode 100755 index b2bf695673..2d7ee37c4d --- a/twake/docker/twake-nginx/entrypoint.sh +++ b/twake/docker/twake-nginx/entrypoint.sh @@ -15,6 +15,8 @@ else fi fi +[[ -d "/etc/nginx/sites-enabled" ]] || mkdir /etc/nginx/sites-enabled + function _selfsigned() { self-signed.sh export NGINX_LISTEN="443 ssl" @@ -43,5 +45,4 @@ export PHP_UPSTREAM envsubst '$${NODE_HOST} $${NGINX_LISTEN}' < /etc/nginx/sites-available/site.template > /etc/nginx/sites-enabled/site echo "upstream php-upstream { server ${PHP_UPSTREAM}; }" > /etc/nginx/conf.d/upstream.conf -cron -f & nginx -g "daemon off;" diff --git a/twake/docker/twake-node/Dockerfile b/twake/docker/twake-node/Dockerfile old mode 100644 new mode 100755 index e779922f4e..344cc8fdaf --- a/twake/docker/twake-node/Dockerfile +++ b/twake/docker/twake-node/Dockerfile @@ -1,27 +1,16 @@ # Common node machine -FROM node:16-bullseye-slim as node-base +FROM node:lts as node-base ### Install dependancies - -#Imagick -RUN apt-get update && apt-get install -y ghostscript && apt-get clean -RUN apt-get update && apt-get install -y graphicsmagick - -#Unoconv -RUN apt-get update && apt-get install -y --force-yes unoconv libxml2-dev -RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -RUN apt-get update && apt-get install wget -# upgrade unoconv -RUN wget -N https://raw.githubusercontent.com/dagwieers/unoconv/master/unoconv -O /usr/bin/unoconv -RUN chmod ugo+x /usr/bin/unoconv -RUN ln -s /usr/bin/python3 /usr/bin/python - +RUN apt-get update && \ + apt-get install -y ghostscript graphicsmagick wget unoconv libxml2-dev ffmpeg python-is-python3 && \ + # upgrade unoconv + wget -N https://raw.githubusercontent.com/dagwieers/unoconv/master/unoconv -O /usr/bin/unoconv && \ + chmod +x /usr/bin/unoconv #Docker mac issue -RUN apt-get update && apt-get install -y libc6 -RUN ln -s /lib/libc.musl-x86_64.so.1 /lib/ld-linux-x86-64.so.2 +# RUN apt-get update && apt-get install -y libc6 +# RUN ln -s /lib/libc.musl-x86_64.so.1 /lib/ld-linux-x86-64.so.2 -# FFmpeg -RUN apt-get install -y ffmpeg ### Install Twake @@ -37,42 +26,39 @@ COPY backend/node/ . # Add frontend Stage FROM node-base as with-frontend -ARG NODE_ENV=production - -ENV NODE_ENV=development -RUN npm install #Install dev dependancies for build COPY backend/node/ . -ENV NODE_ENV=${NODE_ENV} -RUN npm run build #Build in production mode -RUN rm -R node_modules -RUN npm install #Install prod dependancies after build +#Install dev dependancies for build +RUN NODE_ENV=development npm install && \ + #Build in production mode + export NODE_ENV=production && \ + npm run build && \ + rm -rf node_modules && \ + #Install prod dependancies after build + npm install --legacy-peer-deps # Add frontend into node -ENV NODE_ENV=development COPY frontend/ ../public_build/ -RUN apt-get install -y build-essential -RUN cd ../public_build && yarn install --network-timeout 1000000000 -RUN yarn global add webpack && yarn global add webpack-cli && yarn global add jest -RUN cp ../public_build/src/app/environment/environment.ts.dist ../public_build/src/app/environment/environment.ts -ENV NODE_ENV=${NODE_ENV} -RUN cd ../public_build && yarn build -RUN cd ../public_build && rm -R node_modules -RUN mv ../public_build/build/* public/; rm -R ../public_build +RUN export NODE_ENV=development && \ + apt-get install -y build-essential && \ + cd ../public_build/ && \ + yarn install --network-timeout 1000000000 && \ + yarn global add webpack webpack-cli jest && \ + cp src/app/environment/environment.ts.dist src/app/environment/environment.ts && \ + export NODE_ENV=production && \ + yarn build && \ + rm -R node_modules && \ + mv build/* ../app/public && \ + cd .. && rm -R public_build/ # Development Stage FROM with-frontend as development ENV NODE_ENV=development -RUN npm install -g pino-pretty -RUN npm install -g tsc-watch -RUN yarn install -RUN ls -RUN ls public - +RUN npm install -g pino-pretty && \ + npm install -g tsc-watch && \ + yarn install CMD ["npm", "run", "dev:debug"] - - # Production Stage FROM with-frontend as production diff --git a/twake/docker/twake-php/Dockerfile b/twake/docker/twake-php/Dockerfile old mode 100644 new mode 100755 index 27f3474259..dd57e7bd28 --- a/twake/docker/twake-php/Dockerfile +++ b/twake/docker/twake-php/Dockerfile @@ -1,20 +1,18 @@ FROM twaketech/php7.1-cassandra -MAINTAINER Romaric Mourgues +LABEL org.opencontainers.image.authors="Romaric Mourgues ,Nguyen Thai " #Crontab ADD docker/twake-php/crontab /etc/cron.d/twake-cron -RUN chmod 0644 /etc/cron.d/twake-cron -RUN touch /var/log/cron.log - -RUN mkdir /twake-core/ COPY backend/core/ /twake-core/ -RUN cd /twake-core/ && php composer.phar install - COPY docker/twake-php/entrypoint.sh / -RUN chmod 0777 /entrypoint.sh + +RUN chmod 0644 /etc/cron.d/twake-cron && \ + touch /var/log/cron.log && \ + cd /twake-core/ && php composer.phar install && \ + chmod +x /entrypoint.sh #For exec WORKDIR /twake-core/ -ENTRYPOINT /entrypoint.sh "$DEV" +ENTRYPOINT ["/entrypoint.sh", "$DEV"] diff --git a/twake/frontend/package-lock.json b/twake/frontend/package-lock.json index ce31ddc7b1..9b7ad64e23 100644 --- a/twake/frontend/package-lock.json +++ b/twake/frontend/package-lock.json @@ -13260,6 +13260,15 @@ "@types/react": "^16" } }, + "@types/react-page-visibility": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@types/react-page-visibility/-/react-page-visibility-6.4.1.tgz", + "integrity": "sha512-vNlYAqKhB2SU1HmF9ARFTFZN0NSPzWn8HSjBpFqYuQlJhsb/aSYeIZdygeqfSjAg0PZ70id2IFWHGULJwe59Aw==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-redux": { "version": "7.1.24", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", @@ -32524,6 +32533,11 @@ "source-map-js": "^1.0.2" } }, + "postcss-100vh-fix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/postcss-100vh-fix/-/postcss-100vh-fix-1.0.2.tgz", + "integrity": "sha512-t7vqk9AfjI4fXmWlQCEiMZFFhi1hro4WlECINI1TV6Qh1XapUJE++gCmNr95F5Hen/+bz1OmO+SiSB9TZmFmSg==" + }, "postcss-attribute-case-insensitive": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", @@ -36336,6 +36350,14 @@ "prop-types": "^15.7.2" } }, + "react-page-visibility": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/react-page-visibility/-/react-page-visibility-7.0.0.tgz", + "integrity": "sha512-d4Kq/8TtJSr8dQc8EJeAZcSKTrGzC5OPTm6UrMur9BnwP0fgTawI9+Nd+ZGB7vwCfn2yZS0qDF9DR3/QYTGazw==", + "requires": { + "prop-types": "^15.7.2" + } + }, "react-perfect-scrollbar": { "version": "1.5.8", "resolved": "https://registry.npmjs.org/react-perfect-scrollbar/-/react-perfect-scrollbar-1.5.8.tgz", @@ -41935,9 +41957,9 @@ "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" }, "ua-parser-js": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", - "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==" + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" }, "uglify-js": { "version": "3.17.0", diff --git a/twake/frontend/src/app/atoms/link/index.tsx b/twake/frontend/src/app/atoms/link/index.tsx index 6968f600c2..fee0bceb32 100644 --- a/twake/frontend/src/app/atoms/link/index.tsx +++ b/twake/frontend/src/app/atoms/link/index.tsx @@ -25,7 +25,7 @@ export default function A( return ( diff --git a/twake/frontend/src/app/components/drive/ui/file.js b/twake/frontend/src/app/components/drive/ui/file.js index 673a1e6c5a..7d6c35c372 100755 --- a/twake/frontend/src/app/components/drive/ui/file.js +++ b/twake/frontend/src/app/components/drive/ui/file.js @@ -10,6 +10,7 @@ import FileType from './file-type'; import TagPicker from 'components/tag-picker/tag-picker'; import { addApiUrlIfNeeded } from 'app/features/global/utils/URLUtils'; import '../drive.scss'; +import { formatTime } from '@features/global/utils/Numbers'; export default class File extends React.Component { constructor(props) { @@ -20,16 +21,13 @@ export default class File extends React.Component { var date = false; if (this.props.data.modified) { - date = moment(this.props.data.modified * 1000); + date = new Date(this.props.data.modified * 1000); } if (this.props.data.added && this.props.isVersion) { - date = moment(this.props.data.added * 1000); + date = new Date(this.props.data.added * 1000); } - var date_string = date ? date.format('L') : '-'; - if (new Date().getTime() - date.unix() * 1000 < 1000 * 23 * 60 * 60) { - date_string = date ? date.format('LT') : '-'; - } + var date_string = date ? formatTime(date) : '-'; return [
void; className?: string; large?: boolean; + xlarge?: boolean; }; export default ({ @@ -43,6 +44,7 @@ export default ({ status, onRemove, large, + xlarge, }: PropsType) => { const { companyId, workspaceId } = RouterService.getStateFromRoute(); const [file, setFile] = useState(_file); @@ -109,13 +111,34 @@ export default ({ } }; - const computedWidth = file.thumbnail_ratio * 200; + let computedHeight = 200; + let computedWidth = file.thumbnail_ratio * 200; const isMediaFile = ['image', 'video'].includes(file.type); + if (xlarge) { + computedWidth = Math.max( + 160, + Math.min( + Math.min(messageFile.metadata?.thumbnails?.[0]?.width || 600, 600), + file.thumbnail_ratio * document.body.clientHeight * 0.5, + ), + ); + computedHeight = Math.max( + 200, + Math.min( + Math.min( + messageFile.metadata?.thumbnails?.[0]?.height || 10000, + document.body.clientHeight * 0.5, + ), + computedWidth / file.thumbnail_ratio, + ), + ); + } + return (
companyId && onClickFile()} > {large && } diff --git a/twake/frontend/src/app/components/file/file.scss b/twake/frontend/src/app/components/file/file.scss index 4680db91e3..8c1c5432ab 100644 --- a/twake/frontend/src/app/components/file/file.scss +++ b/twake/frontend/src/app/components/file/file.scss @@ -14,8 +14,9 @@ } &.large-view { - height: 200px; - max-width: 400px; + min-height: 200px; + max-height: 500px; + max-width: 90%; min-width: 160px; position: relative; diff --git a/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx b/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx index db63a9f344..d92b617999 100644 --- a/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx +++ b/twake/frontend/src/app/components/twacode/pseudo-markdown-dictionary.tsx @@ -92,7 +92,7 @@ export const DynamicComponent = ({ } return ( // eslint-disable-next-line react/jsx-no-target-blank - + {child} ); @@ -334,9 +334,7 @@ class PseudoMarkdownDictionary { object: (_child: any, object: any) => , }, image: { - object: (_child: any, object: any) => ( - - ), + object: (_child: any, object: any) => , }, icon: { object: (_child: any, object: any) => , diff --git a/twake/frontend/src/app/deprecated/user/NotificationPreferences.ts b/twake/frontend/src/app/deprecated/user/NotificationPreferences.ts index 077170efc4..163cc804da 100644 --- a/twake/frontend/src/app/deprecated/user/NotificationPreferences.ts +++ b/twake/frontend/src/app/deprecated/user/NotificationPreferences.ts @@ -40,11 +40,11 @@ class NotificationPreferencesService extends Observable { ); const isNightBreak = this.isInPeriod(nightBreakIntrv[0], nightBreakIntrv[1]); - const isDeactivate = moment - .unix(this.notificationPreferences.preferences.deactivate_notifications_until) - .diff(moment()); + const isDeactivate = + new Date().getTime() < + this.notificationPreferences.preferences.deactivate_notifications_until; - return isNightBreak ? false : isDeactivate > 0 ? false : true; + return isNightBreak ? false : isDeactivate ? false : true; } return false; @@ -55,9 +55,9 @@ class NotificationPreferencesService extends Observable { * @param timeToAdd Time to add * @param format Unit of time */ - deactivateNotificationsUntil(timeToAdd: number, format: 's' | 'm' | 'h' | 'd' | 'y'): void { + deactivateNotificationsUntil(timeToAddMs: number): void { this.save([ - { key: 'deactivate_notifications_until', value: moment().add(timeToAdd, format).unix() }, + { key: 'deactivate_notifications_until', value: new Date().getTime() + timeToAddMs }, ]); } diff --git a/twake/frontend/src/app/features/global/utils/Numbers.ts b/twake/frontend/src/app/features/global/utils/Numbers.ts index 5d7624d8f4..d1b907b8c8 100755 --- a/twake/frontend/src/app/features/global/utils/Numbers.ts +++ b/twake/frontend/src/app/features/global/utils/Numbers.ts @@ -104,3 +104,27 @@ export default class Numbers { return ret; } } + +export const formatTime = ( + time: number | string, + locale?: string, + options: { keepTime?: boolean; keepSeconds?: boolean; keepDate?: boolean } = { + keepTime: true, + } +) => { + time = new Date(time).getTime(); + locale = locale || navigator.language; + const now = Date.now(); + const year = new Date(time).getFullYear(); + const nowYear = new Date(now).getFullYear(); + const day = 24 * 60 * 60 * 1000; + return new Intl.DateTimeFormat(locale, { + year: nowYear !== year || options?.keepDate ? "numeric" : undefined, + month: now - time >= day || options?.keepDate ? "short" : undefined, + day: now - time >= day || options?.keepDate ? "numeric" : undefined, + hour: now - time < day || options?.keepTime ? "numeric" : undefined, + minute: now - time < day || options?.keepTime ? "numeric" : undefined, + second: options?.keepSeconds ? "numeric" : undefined, + }).format(new Date(time)); +}; + diff --git a/twake/frontend/src/app/views/applications/drive/drive-content.js b/twake/frontend/src/app/views/applications/drive/drive-content.js index 6628f9fcb9..fd517eb212 100755 --- a/twake/frontend/src/app/views/applications/drive/drive-content.js +++ b/twake/frontend/src/app/views/applications/drive/drive-content.js @@ -642,6 +642,7 @@ export default class Drive extends Component { list.push(
(this.upload_zone = node)} disableClick diff --git a/twake/frontend/src/app/views/applications/messages/input/input.tsx b/twake/frontend/src/app/views/applications/messages/input/input.tsx index 65c991a218..ce6d80b99e 100644 --- a/twake/frontend/src/app/views/applications/messages/input/input.tsx +++ b/twake/frontend/src/app/views/applications/messages/input/input.tsx @@ -87,7 +87,11 @@ export default (props: Props) => { id: props.threadId, }); - const { isActive: isBeingQuoted, close } = useMessageQuoteReply(props.channelId || ''); + const { + isActive: isBeingQuoted, + close, + message: messageBeingQuoted, + } = useMessageQuoteReply(props.channelId || ''); const { upload, clear: clearUploads } = useUploadZones(editorId); const format = props.format || 'markdown'; @@ -349,9 +353,13 @@ export default (props: Props) => { } }; + useEffect(() => { + focusEditor(); + }, [messageBeingQuoted]); + const disabled = isEmpty() || isTooLong; return ( -
focus()}> +
focus()}> {isBeingQuoted && close()} />} { target = '_self'; } return ( - + {children} ); diff --git a/twake/frontend/src/app/views/applications/messages/message/parts/LinkPreview.tsx b/twake/frontend/src/app/views/applications/messages/message/parts/LinkPreview.tsx index 9ebef88eb0..96041d9504 100644 --- a/twake/frontend/src/app/views/applications/messages/message/parts/LinkPreview.tsx +++ b/twake/frontend/src/app/views/applications/messages/message/parts/LinkPreview.tsx @@ -34,7 +34,7 @@ export default ({ preview }: PropsType): React.ReactElement => {
{ key={file.metadata?.external_id || file.id} type={'message'} file={file} + xlarge={files.length === 1 && (files[0].metadata?.thumbnails?.length || 0) > 0} large={ //If all the documents are images files.length <= 6 && files.filter( file => file.metadata?.source === 'internal' && - (file.metadata?.thumbnails?.length || 0) > 0 + (file.metadata?.thumbnails?.length || 0) > 0, ).length === files.length } onRemove={() => setFiles(files.filter(f => f.id !== file.id))} diff --git a/twake/frontend/src/app/views/applications/messages/message/parts/MessageHeader.tsx b/twake/frontend/src/app/views/applications/messages/message/parts/MessageHeader.tsx index 780abb975b..2843faba06 100644 --- a/twake/frontend/src/app/views/applications/messages/message/parts/MessageHeader.tsx +++ b/twake/frontend/src/app/views/applications/messages/message/parts/MessageHeader.tsx @@ -122,7 +122,7 @@ export default (props: Props) => { className="date" // eslint-disable-next-line react/jsx-no-target-blank target="_BLANK" - href={messageLink ? addUrlTryDesktop(messageLink) : '#'} + href={(messageLink ? addUrlTryDesktop(messageLink) : '#')?.replace(/^javascript:/, '')} onMouseEnter={() => updateMessageLink()} rel="noreferrer" > diff --git a/twake/frontend/src/app/views/applications/messages/message/parts/Options.tsx b/twake/frontend/src/app/views/applications/messages/message/parts/Options.tsx index 4f8ed5f780..e4849b74db 100644 --- a/twake/frontend/src/app/views/applications/messages/message/parts/Options.tsx +++ b/twake/frontend/src/app/views/applications/messages/message/parts/Options.tsx @@ -132,7 +132,7 @@ export default (props: Props) => { icon: 'corner-down-left', text: Languages.t('scenes.apps.messages.message.reply_button', [], 'Reply'), onClick: () => { - setQuoteReply({ message: message.id, channel: channelId }); + setQuoteReply({ message: message.thread_id, channel: channelId }); }, }); } @@ -336,7 +336,7 @@ export default (props: Props) => {
{ - setQuoteReply({ message: message.id, channel: channelId }); + setQuoteReply({ message: message.thread_id, channel: channelId }); }} > diff --git a/twake/frontend/src/app/views/applications/messages/message/parts/PossiblyPendingAttachment.tsx b/twake/frontend/src/app/views/applications/messages/message/parts/PossiblyPendingAttachment.tsx index a65eda47c8..8ceb95b22c 100644 --- a/twake/frontend/src/app/views/applications/messages/message/parts/PossiblyPendingAttachment.tsx +++ b/twake/frontend/src/app/views/applications/messages/message/parts/PossiblyPendingAttachment.tsx @@ -11,9 +11,10 @@ type PropsType = { onRemove?: () => void; type: 'input' | 'message'; large?: boolean; + xlarge?: boolean; }; -export default ({ file, onRemove, type, large }: PropsType) => { +export default ({ file, onRemove, type, large, xlarge }: PropsType) => { const { getOnePendingFile } = useUpload(); const id = @@ -68,6 +69,7 @@ export default ({ file, onRemove, type, large }: PropsType) => { file={formatedFile} messageFile={file} large={large} + xlarge={xlarge} status={status} progress={progress} onRemove={onRemove} diff --git a/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx b/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx index 605f60b9ff..1f765f1730 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Parts/Channel/ChannelIntermediate.tsx @@ -51,6 +51,7 @@ export default (props: Props): JSX.Element => { mentions={ notifications.filter( n => + !n.mention_type || (n.mention_type === 'me' && ['mentions', 'me', 'all'].includes(channel.user_member?.notification_level || '')) || (n.mention_type === 'global' && diff --git a/twake/frontend/src/app/views/client/channels-bar/Parts/CurrentUser/Notifications/NotificationBell.tsx b/twake/frontend/src/app/views/client/channels-bar/Parts/CurrentUser/Notifications/NotificationBell.tsx index 3e276814f1..d4752c4aed 100644 --- a/twake/frontend/src/app/views/client/channels-bar/Parts/CurrentUser/Notifications/NotificationBell.tsx +++ b/twake/frontend/src/app/views/client/channels-bar/Parts/CurrentUser/Notifications/NotificationBell.tsx @@ -29,18 +29,20 @@ export default (props: PropsType) => { { type: 'menu', text: Languages.t('scenes.app.channelsbar.currentuser.disabling_notifications'), - onClick: () => NotificationPreferences.deactivateNotificationsUntil(1, 'h'), + onClick: () => NotificationPreferences.deactivateNotificationsUntil(1 * 60 * 60 * 1000), }, { type: 'menu', text: '2 hours', // Add translation - onClick: () => NotificationPreferences.deactivateNotificationsUntil(2, 'h'), + onClick: () => NotificationPreferences.deactivateNotificationsUntil(2 * 60 * 60 * 1000), }, { type: 'menu', text: Languages.t('scenes.app.channelsbar.currentuser.disabling_notifications_until'), onClick: () => - NotificationPreferences.deactivateNotificationsUntil(hoursUntilTomorrowMorning, 'h'), + NotificationPreferences.deactivateNotificationsUntil( + hoursUntilTomorrowMorning * 60 * 60 * 1000, + ), }, { type: 'menu', @@ -51,8 +53,8 @@ export default (props: PropsType) => { ), onClick: () => { status - ? NotificationPreferences.deactivateNotificationsUntil(24, 'y') - : NotificationPreferences.deactivateNotificationsUntil(0, 's'); + ? NotificationPreferences.deactivateNotificationsUntil(10 * 365 * 24 * 60 * 60 * 1000) + : NotificationPreferences.deactivateNotificationsUntil(0); }, }, { diff --git a/twake/frontend/src/app/views/client/channels-bar/Parts/Footer/Footer.js b/twake/frontend/src/app/views/client/channels-bar/Parts/Footer/Footer.js index a911830ade..afd3fcf09f 100755 --- a/twake/frontend/src/app/views/client/channels-bar/Parts/Footer/Footer.js +++ b/twake/frontend/src/app/views/client/channels-bar/Parts/Footer/Footer.js @@ -14,13 +14,17 @@ export default class Footer extends React.Component { super(); this.onpremise = !!((InitService.server_infos || {}).branding || {}).name; this.menu = [ - { type:"text", text: `Twake v${Version.version_detail}` }, - { - type: 'menu', - text: Languages.t('general.help.support', [], 'Support'), - icon: 'comment', - onClick: props.onClickHelp, - }, + { type: 'text', text: `Twake v${Version.version_detail}` }, + ...(props.onClickHelp + ? [ + { + type: 'menu', + text: Languages.t('general.help.support', [], 'Support'), + icon: 'comment', + onClick: props.onClickHelp, + }, + ] + : []), { type: 'menu', text: Languages.t('general.help.documentation', [], 'Documentation'), diff --git a/twake/frontend/src/app/views/integration/components/apps.js b/twake/frontend/src/app/views/integration/components/apps.js index 18b45f76cd..9ea65fbcc5 100644 --- a/twake/frontend/src/app/views/integration/components/apps.js +++ b/twake/frontend/src/app/views/integration/components/apps.js @@ -38,8 +38,13 @@ export default function Apps(props) { >
{props.apps.map(item => ( - // eslint-disable-next-line react/jsx-no-target-blank - +
{item.name}
diff --git a/twake/frontend/yarn.lock b/twake/frontend/yarn.lock index 80a325651a..f90bd67bd5 100644 --- a/twake/frontend/yarn.lock +++ b/twake/frontend/yarn.lock @@ -4559,6 +4559,13 @@ dependencies: "@types/react" "^16" +"@types/react-page-visibility@^6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@types/react-page-visibility/-/react-page-visibility-6.4.1.tgz#21c3bc4a3f310d38d188916cadc55f2bde65f27d" + integrity sha512-vNlYAqKhB2SU1HmF9ARFTFZN0NSPzWn8HSjBpFqYuQlJhsb/aSYeIZdygeqfSjAg0PZ70id2IFWHGULJwe59Aw== + dependencies: + "@types/react" "*" + "@types/react-redux@^7.1.20", "@types/react-redux@^7.1.7": version "7.1.24" resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz" @@ -18245,6 +18252,13 @@ react-outside-click-handler@^1.3.0: object.values "^1.1.0" prop-types "^15.7.2" +react-page-visibility@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/react-page-visibility/-/react-page-visibility-7.0.0.tgz#13dfe604790d061e70b900038bad1ca769a36cbc" + integrity sha512-d4Kq/8TtJSr8dQc8EJeAZcSKTrGzC5OPTm6UrMur9BnwP0fgTawI9+Nd+ZGB7vwCfn2yZS0qDF9DR3/QYTGazw== + dependencies: + prop-types "^15.7.2" + react-perfect-scrollbar@^1.5.8: version "1.5.8" resolved "https://registry.npmjs.org/react-perfect-scrollbar/-/react-perfect-scrollbar-1.5.8.tgz" @@ -21011,9 +21025,9 @@ typescript@~3.9.7: integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== ua-parser-js@^0.7.18: - version "0.7.31" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz" - integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== uglify-js@^3.1.4: version "3.16.0"