From 73d94f8ccc729adb3046efb9279fc81191c2e011 Mon Sep 17 00:00:00 2001 From: ericgaspar Date: Thu, 14 Apr 2022 14:04:52 +0200 Subject: [PATCH] First commit --- LICENSE | 661 +++++++++++++++++++++++++++++ README.md | 84 ++++ README_fr.md | 66 +++ check_process | 24 ++ conf/app.src | 7 + conf/config.ini | 5 + conf/nginx.conf | 25 ++ conf/php-fpm.conf | 430 +++++++++++++++++++ config_panel.toml.example | 295 +++++++++++++ doc/.gitkeep | 0 doc/DESCRIPTION.md | 1 + doc/DISCLAIMER.md | 0 doc/screenshots/.gitkeep | 0 doc/screenshots/screenshot.png | Bin 0 -> 57441 bytes manifest.json | 49 +++ scripts/_common.sh | 22 + scripts/backup | 69 +++ scripts/change_url | 105 +++++ scripts/config | 102 +++++ scripts/install | 144 +++++++ scripts/remove | 80 ++++ scripts/restore | 117 +++++ scripts/upgrade | 110 +++++ sources/extra_files/app/.gitignore | 2 + sources/patches/.gitignore | 2 + 25 files changed, 2400 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 README_fr.md create mode 100644 check_process create mode 100644 conf/app.src create mode 100644 conf/config.ini create mode 100644 conf/nginx.conf create mode 100644 conf/php-fpm.conf create mode 100644 config_panel.toml.example create mode 100644 doc/.gitkeep create mode 100644 doc/DESCRIPTION.md create mode 100644 doc/DISCLAIMER.md create mode 100644 doc/screenshots/.gitkeep create mode 100644 doc/screenshots/screenshot.png create mode 100644 manifest.json create mode 100644 scripts/_common.sh create mode 100755 scripts/backup create mode 100644 scripts/change_url create mode 100644 scripts/config create mode 100755 scripts/install create mode 100755 scripts/remove create mode 100755 scripts/restore create mode 100644 scripts/upgrade create mode 100644 sources/extra_files/app/.gitignore create mode 100644 sources/patches/.gitignore diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dba13ed --- /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/README.md b/README.md new file mode 100644 index 0000000..820e437 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ + +# Packaging an app, starting from this example + +- Copy this app before working on it, using the ['Use this template'](https://github.com/YunoHost/example_ynh/generate) button on the Github repo. +- Edit the `manifest.json` with app specific info. +- Edit the `install`, `upgrade`, `remove`, `backup`, and `restore` scripts, and any relevant conf files in `conf/`. + - Using the [script helpers documentation.](https://yunohost.org/packaging_apps_helpers) +- Add a `LICENSE` file for the package. +- Edit `doc/DISCLAIMER*.md` +- The `README.md` files are to be automatically generated by https://github.com/YunoHost/apps/tree/master/tools/README-generator + + +--- + + + +# Example app for YunoHost + +[![Integration level](https://dash.yunohost.org/integration/example.svg)](https://dash.yunohost.org/appci/app/example) ![](https://ci-apps.yunohost.org/ci/badges/example.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/example.maintain.svg) +[![Install example with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=example) + +*[Lire ce readme en français.](./README_fr.md)* + +> *This package allows you to install example quickly and simply on a YunoHost server. +If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/install) to learn how to install it.* + +## Overview + +Explain in *a few (10~15) words* the purpose of the app or what it actually does (it is meant to give a rough idea to users browsing a catalog of 100+ apps) + +**Shipped version:** 1.0~ynh1 + +**Demo:** https://demo.example.com + + +## Screenshots + + + ![](./doc/screenshots/example.jpg) + + + + +## Disclaimers / important information + +* Any known limitations, constrains or stuff not working, such as (but not limited to): + * requiring a full dedicated domain ? + * architectures not supported ? + * not-working single-sign on or LDAP integration ? + * the app requires an important amount of RAM / disk / .. to install or to work properly + * etc... + +* Other infos that people should be aware of, such as: + * any specific step to perform after installing (such as manually finishing the install, specific admin credentials, ...) + * how to configure / administrate the application if it ain't obvious + * upgrade process / specificities / things to be aware of ? + * security considerations ? + + + +## Documentation and resources + +* Official app website: https://example.com +* Official user documentation: https://yunohost.org/apps +* Official admin documentation: https://yunohost.org/packaging_apps +* Upstream app code repository: https://some.forge.com/example/example +* YunoHost documentation for this app: https://yunohost.org/app_example +* Report a bug: https://github.com/YunoHost-Apps/example_ynh/issues + +## Developer info + +Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/example_ynh/tree/testing). + +To try the testing branch, please proceed like that. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/example_ynh/tree/testing --debug +or +sudo yunohost app upgrade example -u https://github.com/YunoHost-Apps/example_ynh/tree/testing --debug +``` + +**More info regarding app packaging:** https://yunohost.org/packaging_apps diff --git a/README_fr.md b/README_fr.md new file mode 100644 index 0000000..d856bf9 --- /dev/null +++ b/README_fr.md @@ -0,0 +1,66 @@ +# Example app pour YunoHost + +[![Niveau d'intégration](https://dash.yunohost.org/integration/example.svg)](https://dash.yunohost.org/appci/app/example) ![](https://ci-apps.yunohost.org/ci/badges/example.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/example.maintain.svg) +[![Installer example avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=example) + +*[Read this readme in english.](./README.md)* +*[Lire ce readme en français.](./README_fr.md)* + +> *This package allows you to install example quickly and simply on a YunoHost server. +If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/install) to learn how to install it.* + +## Vue d'ensemble + +Expliquez en *quelques* (10~15) mots l'utilité de l'app ou ce qu'elle fait (l'objectif est de donner une idée grossière pour des utilisateurs qui naviguent dans un catalogue de 100+ apps) + +**Version incluse:** 1.0~ynh1 + +**Démo:** https://demo.example.com + + +## Captures d'écran + + + ![](./doc/screenshots/example.jpg) + + + + +## Avertissements / informations importantes + +* Any known limitations, constrains or stuff not working, such as (but not limited to): + * requiring a full dedicated domain ? + * architectures not supported ? + * not-working single-sign on or LDAP integration ? + * the app requires an important amount of RAM / disk / .. to install or to work properly + * etc... + +* Other infos that people should be aware of, such as: + * any specific step to perform after installing (such as manually finishing the install, specific admin credentials, ...) + * how to configure / administrate the application if it ain't obvious + * upgrade process / specificities / things to be aware of ? + * security considerations ? + + + +## Documentations et ressources + +* Site official de l'app : https://example.com +* Documentation officielle utilisateur: https://yunohost.org/apps +* Documentation officielle de l'admin: https://yunohost.org/packaging_apps +* Dépôt de code officiel de l'app: https://some.forge.com/example/example +* Documentation YunoHost pour cette app: https://yunohost.org/app_example +* Signaler un bug: https://github.com/YunoHost-Apps/example_ynh/issues + +## Informations pour les développeurs + +Merci de faire vos pull request sur la [branche testing](https://github.com/YunoHost-Apps/example_ynh/tree/testing). + +Pour essayer la branche testing, procédez comme suit. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/example_ynh/tree/testing --debug +or +sudo yunohost app upgrade example -u https://github.com/YunoHost-Apps/example_ynh/tree/testing --debug +``` + +**Plus d'infos sur le packaging d'applications:** https://yunohost.org/packaging_apps \ No newline at end of file diff --git a/check_process b/check_process new file mode 100644 index 0000000..c453624 --- /dev/null +++ b/check_process @@ -0,0 +1,24 @@ +;; Test complet + ; Manifest + domain="domain.tld" + path="/path" + is_public=1 + ; Checks + pkg_linter=1 + setup_sub_dir=1 + setup_root=1 + setup_nourl=0 + setup_private=1 + setup_public=1 + upgrade=1 + #upgrade=1 from_commit=CommitHash + backup_restore=1 + multi_instance=1 + change_url=1 +;;; Options +Email= +Notification=none +;;; Upgrade options + ; commit=CommitHash + name=Name and date of the commit. + manifest_arg=domain=DOMAIN&path=PATH&is_public=1&language=fr&admin=USER&password=pass&port=666& diff --git a/conf/app.src b/conf/app.src new file mode 100644 index 0000000..b6da560 --- /dev/null +++ b/conf/app.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://github.com/24eme/signaturepdf/archive/3327a64774c58c36103bbda4953521bc6db7e12c.zip +SOURCE_SUM=c7c5941d749d51819bc8b97332d46d1e014b068306d3a9ba789a62c6069e1f33 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=zip +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME=signaturepdf +SOURCE_EXTRACT=true diff --git a/conf/config.ini b/conf/config.ini new file mode 100644 index 0000000..f09a24b --- /dev/null +++ b/conf/config.ini @@ -0,0 +1,5 @@ +[globals] + +# Path to which stored pdf to activate the mode of sharing a signature to several. +# To deactivate this mode, simply do not configure it or leave it empty +PDF_STORAGE_PATH=__DATADIR__ diff --git a/conf/nginx.conf b/conf/nginx.conf new file mode 100644 index 0000000..36722c7 --- /dev/null +++ b/conf/nginx.conf @@ -0,0 +1,25 @@ +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + + # Path to source + alias __FINALPATH__/public/ ; + + index index.php; + + client_max_body_size 50M; + + try_files $uri $uri/ index.php; + location ~ [^/]\.php(/|$) { + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_pass unix:/var/run/php/php__PHPVERSION__-fpm-__NAME__.sock; + + fastcgi_index index.php; + include fastcgi_params; + fastcgi_param REMOTE_USER $remote_user; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param SCRIPT_FILENAME $request_filename; + } + + # Include SSOWAT user panel. + include conf.d/yunohost_panel.conf.inc; +} diff --git a/conf/php-fpm.conf b/conf/php-fpm.conf new file mode 100644 index 0000000..b4c2372 --- /dev/null +++ b/conf/php-fpm.conf @@ -0,0 +1,430 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[__NAMETOCHANGE__] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = __USER__ +group = __USER__ + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = /var/run/php/php__PHPVERSION__-fpm-__NAMETOCHANGE__.sock + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = www-data +listen.group = www-data +;listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user +; or group is differrent than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php/7.0/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +request_terminate_timeout = 1d + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +chdir = __FINALPATH__ + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M + +; Common values to change to increase file upload limit +php_admin_value[upload_max_filesize] = 50M +php_admin_value[post_max_size] = 50M +; php_admin_flag[mail.add_x_header] = Off + +; Other common parameters +; php_admin_value[max_execution_time] = 600 +; php_admin_value[max_input_time] = 300 +; php_admin_value[memory_limit] = 256M +; php_admin_flag[short_open_tag] = On diff --git a/config_panel.toml.example b/config_panel.toml.example new file mode 100644 index 0000000..c6bccd8 --- /dev/null +++ b/config_panel.toml.example @@ -0,0 +1,295 @@ + +## Config panel are available from webadmin > Apps > YOUR_APP > Config Panel Button +## Those panels let user configure some params on their apps using a friendly interface, +## and remove the need to manually edit files from the command line. + +## From a packager perspective, this .toml is coupled to the scripts/config script, +## which may be used to define custom getters/setters. However, most use cases +## should be covered automagically by the core, thus it may not be necessary +## to define a scripts/config at all! + +## ----------------------------------------------------------------------------- +## IMPORTANT: In accordance with YunoHost's spirit, please keep things simple and +## do not overwhelm the admin with tons of misunderstandable or advanced settings. +## ----------------------------------------------------------------------------- + +## The top level describe the entire config panels screen. + +## The version is a required property. +## Here a small reminder to associate config panel version with YunoHost version +## | Config | YNH | Config panel small change log | +## | ------ | --- | ------------------------------------------------------- | +## | 0.1 | 3.x | 0.1 config script not compatible with YNH >= 4.3 | +## | 1.0 | 4.3.x | The new config panel system with 'bind' property | +version = "1.0" + +## (optional) i18n property let you internationalize questions, however this feature +## is only available in core configuration panel (like yunohost domain config). +## So in app config panel this key is ignored for now, but you can internationalize +## by using a lang dictionary (see property name bellow) +# i18n = "prefix_translation_key" + +################################################################################ +#### ABOUT PANELS +################################################################################ + +## The next level describes web admin panels +## You have to choose an ID for each panel, in this example the ID is "main" +## Keep in mind this ID will be used in CLI to refer to your question, so choose +## something short and meaningfull. +## In the webadmin, each panel corresponds to a distinct tab / form +[main] + +## Define the label for your panel +## Internationalization works similarly to the 'description' and 'ask' questions in the manifest +# name.en = "Main configuration" +# name.fr = "Configuration principale" + +## (optional) If you need to trigger a service reload-or-restart after the user +## change a question in this panel, you can add your service in the list. +services = ["__APP__"] +# or services = ["nginx", "__APP__"] to also reload-or-restart nginx + +## (optional) This help properties is a short help displayed on the same line +## than the panel title but not displayed in the tab. +# help = "" + + ############################################################################ + #### ABOUT SECTIONS + ############################################################################ + + ## A panel is composed of one or several sections. + ## + ## Sections are meant to group questions together when they correspond to + ## a same subtopic. This impacts the rendering in terms of CLI prompts + ## and HTML forms + ## + ## You should choose an ID for your section, and prefix it with the panel ID + ## (Be sure to not make a typo in the panel ID, which would implicitly create + ## an other entire panel) + ## + ## We use the context of pepettes_ynh as an example, + ## which is a simple donation form app written in python, + ## and for which the admin will want to edit the configuration + [main.customization] + + ## (optional) Defining a proper title for sections is not mandatory + ## and depends on the exact rendering you're aiming for the CLI / webadmin + name = "" + + ## (optional) This help properties is a short help displayed on the same line + ## than the section title, meant to provide additional details + # help = "" + + ## (optional) As for panel, you can specify to trigger a service + ## reload-or-restart after the user change a question in this section. + ## This property is added to the panel property, it doesn't deactivate it. + ## So no need to replicate, the service list from panel services property. + # services = [] + + ## (optional) By default all questions are optionals, but you can specify a + ## default behaviour for question in the section + optional = false + + ## (optional) It's also possible with the 'visible' property to only + ## display the section depending on the user's answers to previous questions. + ## + ## Be careful that the 'visible' property should only refer to **previous** questions + ## Hence, it should not make sense to have a "visible" property on the very first section. + ## + ## Also, keep in mind that this feature only works in the webadmin and not in CLI + ## (therefore a user could be prompted in CLI for a question that may not be relevant) + # visible = true + + ######################################################################## + #### ABOUT QUESTIONS + ######################################################################## + + ## A section is compound of one or several questions. + + ## --------------------------------------------------------------------- + ## IMPORTANT: as for panel and section you have to choose an ID, but this + ## one should be unique in all this document, even if the question is in + ## an other panel. + ## --------------------------------------------------------------------- + + ## You can use same questions types and properties than in manifest.yml + ## install part. However, in YNH 4.3, a lot of change has been made to + ## extend availables questions types list. + ## See: TODO DOC LINK + + [main.customization.project_name] + + ## (required) The ask property is equivalent to the ask property in + ## the manifest. However, in config panels, questions are displayed on the + ## left side and therefore have less space to be rendered. Therefore, + ## it is better to use a short question, and use the "help" property to + ## provide additional details if necessary. + ask.en = "Name of the project" + + ## (required) The type property indicates how the question should be + ## displayed, validated and managed. Some types have specific properties. + ## + ## Types available: string, boolean, number, range, text, password, path + ## email, url, date, time, color, select, domain, user, tags, file. + ## + ## For a complete list with specific properties, see: TODO DOC LINK + type = "string" + + ######################################################################## + #### ABOUT THE BIND PROPERTY + ######################################################################## + + ## (recommended) 'bind' property is a powerful feature that let you + ## configure how and where the data will be read, validated and written. + + ## By default, 'bind property is in "settings" mode, it means it will + ## **only** read and write the value in application settings file. + ## bind = "settings" + + ## However, settings usually correspond to key/values in actual app configurations + ## Hence, a more useful mode is to have bind = ":FILENAME". In that case, YunoHost + ## will automagically find a line with "KEY=VALUE" in FILENAME + ## (with the adequate separator between KEY and VALUE) + ## + ## YunoHost will then use this value for the read/get operation. + ## During write/set operations, YunoHost will overwrite the value + ## in **both** FILENAME and in the app's settings.yml + + ## Configuration file format supported: yaml, toml, json, ini, env, php, + ## python. The feature probably works with others formats, but should be tested carefully. + + ## Note that this feature only works with relatively simple cases + ## such as `KEY: VALUE`, but won't properly work with + ## complex data structures like multilin array/lists or dictionnaries. + ## It also doesn't work with XML format, custom config function call, php define(), ... + + ## More info on TODO + # bind = ":/var/www/__APP__/settings.py" + + + ## By default, bind = ":FILENAME" will use the question ID as KEY + ## ... but the question ID may sometime not be the exact KEY name in the configuration file. + ## + ## In particular, in pepettes, the python variable is 'name' and not 'project_name' + ## (c.f. https://github.com/YunoHost-Apps/pepettes_ynh/blob/5cc2d3ffd6529cc7356ff93af92dbb6785c3ab9a/conf/settings.py##L11 ) + ## + ## In that case, the key name can be specified before the column ':' + + bind = "name:/var/www/__APP__/settings.py" + + ## --------------------------------------------------------------------- + ## IMPORTANT: other 'bind' mode exists: + ## + ## bind = "FILENAME" (with no column character before FILENAME) + ## may be used to bind to the **entire file content** (instead of a single KEY/VALUE) + ## This could be used to expose an entire configuration file, or binary files such as images + ## For example: + ## bind = "/var/www/__APP__/img/logo.png" + ## + ## bind = "null" can be used to disable reading / writing in settings. + ## This creates sort of a "virtual" or "ephemeral" question which is not related to any actual setting + ## In this mode, you are expected to define custom getter/setters/validators in scripts/config: + ## + ## getter: get__QUESTIONID() + ## setter: set__QUESTIONID() + ## validator: validate__QUESTIONID() + ## + ## You can also specify a common getter / setter / validator, with the + ## function 'bind' mode, for example here it will try to run + ## get__array_settings() first. + # bind = "array_settings()" + ## --------------------------------------------------------------------- + + ## --------------------------------------------------------------------- + ## IMPORTANT: with the exception of bind=null questions, + ## question IDs should almost **always** correspond to an app setting + ## initialized / reused during install/upgrade. + ## Not doing so may result in inconsistencies between the config panel mechanism + ## and the use of ynh_add_config + ## --------------------------------------------------------------------- + + ######################################################################## + #### OTHER GENERIC PROPERTY FOR QUESTIONS + ######################################################################## + + ## (optional) An help text for the question + help = "Fill the name of the project which will received donation" + + ## (optional) An example display as placeholder in web form + # example = "YunoHost" + + ## (optional) set to true in order to redact the value in operation logs + # redact = false + + ## (optional) A validation pattern + ## --------------------------------------------------------------------- + ## IMPORTANT: your pattern should be between simple quote, not double. + ## --------------------------------------------------------------------- + pattern.regexp = '^\w{3,30}$' + pattern.error = "The name should be at least 3 chars and less than 30 chars. Alphanumeric chars are accepted" + + ## Note: visible and optional properties are also available for questions + + + [main.customization.contact_url] + ask = "Contact url" + type = "url" + example = "mailto: contact@example.org" + help = "mailto: accepted" + pattern.regexp = '^mailto:[^@]+@[^@]+|https://$' + pattern.error = "Should be https or mailto:" + bind = ":/var/www/__APP__/settings.py" + + [main.customization.logo] + ask = "Logo" + type = "file" + accept = ".png" + help = "Fill with an already resized logo" + bind = "__FINALPATH__/img/logo.png" + + [main.customization.favicon] + ask = "Favicon" + type = "file" + accept = ".png" + help = "Fill with an already sized favicon" + bind = "__FINALPATH__/img/favicon.png" + + + [main.stripe] + name = "Stripe general info" + optional = false + + # The next alert is overwrited with a getter from the config script + [main.stripe.amount] + ask = "Donation in the month : XX € + type = "alert" + style = "success" + + [main.stripe.publishable_key] + ask = "Publishable key" + type = "string" + redact = true + help = "Indicate here the stripe publishable key" + bind = ":/var/www/__APP__/settings.py" + + [main.stripe.secret_key] + ask = "Secret key" + type = "string" + redact = true + help = "Indicate here the stripe secret key" + bind = ":/var/www/__APP__/settings.py" + + [main.stripe.prices] + ask = "Prices ID" + type = "tags" + help = """\ + Indicates here the prices ID of donation products you created in stripe interfaces. \ + Go on [Stripe products](https://dashboard.stripe.com/products) to create those donation products. \ + Fill it tag with 'FREQUENCY/CURRENCY/PRICE_ID' \ + FREQUENCY: 'one_time' or 'recuring' \ + CURRENCY: 'EUR' or 'USD' \ + PRICE_ID: ID from stripe interfaces starting with 'price_' \ + """ + pattern.regexp = '^(one_time|recuring)/(EUR|USD)/price_.*$' + pattern.error = "Please respect the format describe in help text for each price ID" diff --git a/doc/.gitkeep b/doc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/DESCRIPTION.md b/doc/DESCRIPTION.md new file mode 100644 index 0000000..198635c --- /dev/null +++ b/doc/DESCRIPTION.md @@ -0,0 +1 @@ +Logiciel web libre permettant de signer un PDF. \ No newline at end of file diff --git a/doc/DISCLAIMER.md b/doc/DISCLAIMER.md new file mode 100644 index 0000000..e69de29 diff --git a/doc/screenshots/.gitkeep b/doc/screenshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/screenshots/screenshot.png b/doc/screenshots/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c0bce4dd9851c9d7a78c4ac3a6b8947f568f2a15 GIT binary patch literal 57441 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 Om&~m%RGzzi@BaW1EDn= 4.3.0" + }, + "multi_instance": true, + "services": [ + "nginx", + "php7.4-fpm" + ], + "arguments": { + "install" : [ + { + "name": "domain", + "type": "domain" + }, + { + "name": "path", + "type": "path", + "example": "/signaturepdf", + "default": "/signaturepdf" + }, + { + "name": "is_public", + "type": "boolean", + "default": true + } + ] + } +} diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..2f49947 --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +#================================================= +# COMMON VARIABLES +#================================================= + +YNH_PHP_VERSION="7.4" + +# dependencies used by the app +pkg_dependencies="librsvg2-bin pdftk imagemagick potrace" + +#================================================= +# PERSONAL HELPERS +#================================================= + +#================================================= +# EXPERIMENTAL HELPERS +#================================================= + +#================================================= +# FUTURE OFFICIAL HELPERS +#================================================= diff --git a/scripts/backup b/scripts/backup new file mode 100755 index 0000000..ff029f0 --- /dev/null +++ b/scripts/backup @@ -0,0 +1,69 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +# Keep this path for calling _common.sh inside the execution's context of backup and restore scripts +source ../settings/scripts/_common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +ynh_clean_setup () { + ### Remove this function if there's nothing to clean before calling the remove script. + true +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_print_info --message="Loading installation settings..." + +app=$YNH_APP_INSTANCE_NAME + +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +domain=$(ynh_app_setting_get --app=$app --key=domain) +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) +datadir=$(ynh_app_setting_get --app=$app --key=datadir) + +#================================================= +# DECLARE DATA AND CONF FILES TO BACKUP +#================================================= +ynh_print_info --message="Declaring files to be backed up..." + +#================================================= +# BACKUP THE APP MAIN DIR +#================================================= + +ynh_backup --src_path="$final_path" + +#================================================= +# BACKUP THE DATA DIR +#================================================= + +ynh_backup --src_path="$datadir" --is_big + +#================================================= +# BACKUP THE NGINX CONFIGURATION +#================================================= + +ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# BACKUP THE PHP-FPM CONFIGURATION +#================================================= + +ynh_backup --src_path="/etc/php/$phpversion/fpm/pool.d/$app.conf" + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_print_info --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)." diff --git a/scripts/change_url b/scripts/change_url new file mode 100644 index 0000000..51a3236 --- /dev/null +++ b/scripts/change_url @@ -0,0 +1,105 @@ +#!/bin/bash + +#================================================= +# GENERIC STARTING +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# RETRIEVE ARGUMENTS +#================================================= + +old_domain=$YNH_APP_OLD_DOMAIN +old_path=$YNH_APP_OLD_PATH + +new_domain=$YNH_APP_NEW_DOMAIN +new_path=$YNH_APP_NEW_PATH + +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --time --weight=1 + +# Needed for helper "ynh_add_nginx_config" +final_path=$(ynh_app_setting_get --app=$app --key=final_path) + +#================================================= +# BACKUP BEFORE CHANGE URL THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before changing its URL (may take a while)..." --time --weight=1 + +# Backup the current version of the app +ynh_backup_before_upgrade +ynh_clean_setup () { + # Remove the new domain config file, the remove script won't do it as it doesn't know yet its location. + ynh_secure_remove --file="/etc/nginx/conf.d/$new_domain.d/$app.conf" + + # Restore it if the upgrade fails + ynh_restore_upgradebackup +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# CHECK WHICH PARTS SHOULD BE CHANGED +#================================================= + +change_domain=0 +if [ "$old_domain" != "$new_domain" ] +then + change_domain=1 +fi + +change_path=0 +if [ "$old_path" != "$new_path" ] +then + change_path=1 +fi + +#================================================= +# MODIFY URL IN NGINX CONF +#================================================= +ynh_script_progression --message="Updating NGINX web server configuration..." --time --weight=1 + +nginx_conf_path=/etc/nginx/conf.d/$old_domain.d/$app.conf + +# Change the path in the NGINX config file +if [ $change_path -eq 1 ] +then + # Make a backup of the original NGINX config file if modified + ynh_backup_if_checksum_is_different --file="$nginx_conf_path" + # Set global variables for NGINX helper + domain="$old_domain" + path_url="$new_path" + # Create a dedicated NGINX config + ynh_add_nginx_config +fi + +# Change the domain for NGINX +if [ $change_domain -eq 1 ] +then + # Delete file checksum for the old conf file location + ynh_delete_file_checksum --file="$nginx_conf_path" + mv $nginx_conf_path /etc/nginx/conf.d/$new_domain.d/$app.conf + # Store file checksum for the new config file location + ynh_store_file_checksum --file="/etc/nginx/conf.d/$new_domain.d/$app.conf" +fi + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --time --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Change of URL completed for $app" --time --last diff --git a/scripts/config b/scripts/config new file mode 100644 index 0000000..b9e79f8 --- /dev/null +++ b/scripts/config @@ -0,0 +1,102 @@ +#!/bin/bash +# In simple cases, you don't need a config script. + +# With a simple config_panel.toml, you can write in the app settings, in the +# upstream config file or replace complete files (logo ...) and restart services. + +# The config scripts allows you to go further, to handle specific cases +# (validation of several interdependent fields, specific getter/setter for a value, +# display dynamic informations or choices, pre-loading of config type .cube... ). + +#================================================= +# GENERIC STARTING +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source /usr/share/yunohost/helpers + +ynh_abort_if_errors + +#================================================= +# RETRIEVE ARGUMENTS +#================================================= + +final_path=$(ynh_app_setting_get $app final_path) + +#================================================= +# SPECIFIC GETTERS FOR TOML SHORT KEY +#================================================= + +get__amount() { + # Here we can imagine to have an API call to stripe to know the amount of donation during a month + local amount = 200 + + # It's possible to change some properties of the question by overriding it: + if [ $amount -gt 100 ] + then + cat << EOF +style: success +value: $amount +ask: + en: A lot of donation this month: **$amount €** +EOF + else + cat << EOF +style: danger +value: $amount +ask: + en: Not so much donation this month: $amount € +EOF + fi +} + +get__prices() { + local prices = "$(grep "DONATION\['" "$final_path/settings.py" | sed -r "s@^DONATION\['([^']*)'\]\['([^']*)'\] = '([^']*)'@\1/\2/\3@g" | sed -z 's/\n/,/g;s/,$/\n/')" + if [ "$prices" == "," ]; + then + # Return YNH_NULL if you prefer to not return a value at all. + echo YNH_NULL + else + echo $prices + fi +} + + +#================================================= +# SPECIFIC VALIDATORS FOR TOML SHORT KEYS +#================================================= +validate__publishable_key() { + + # We can imagine here we test if the key is really a publisheable key + (is_secret_key $publishable_key) && + echo 'This key seems to be a secret key' +} + +#================================================= +# SPECIFIC SETTERS FOR TOML SHORT KEYS +#================================================= +set__prices() { + + #--------------------------------------------- + # IMPORTANT: setter are trigger only if a change is detected + #--------------------------------------------- + for price in $(echo $prices | sed "s/,/ /"); do + frequency=$(echo $price | cut -d/ -f1) + currency=$(echo $price | cut -d/ -f2) + price_id=$(echo $price | cut -d/ -f3) + sed "d/DONATION\['$frequency'\]\['$currency'\]" "$final_path/settings.py" + + echo "DONATION['$frequency']['$currency'] = '$price_id'" >> "$final_path/settings.py" + done + + #--------------------------------------------- + # IMPORTANT: to be able to upgrade properly, you have to saved the value in settings too + #--------------------------------------------- + ynh_app_setting_set $app prices $prices +} + +#================================================= +# GENERIC FINALIZATION +#================================================= +ynh_app_config_run $1 diff --git a/scripts/install b/scripts/install new file mode 100755 index 0000000..d6ed3c3 --- /dev/null +++ b/scripts/install @@ -0,0 +1,144 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +ynh_clean_setup () { + ### Remove this function if there's nothing to clean before calling the remove script. + true +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= + +domain=$YNH_APP_ARG_DOMAIN +path_url=$YNH_APP_ARG_PATH +is_public=$YNH_APP_ARG_IS_PUBLIC + +app=$YNH_APP_INSTANCE_NAME + +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +ynh_script_progression --message="Validating installation parameters..." --weight=1 + +final_path=/var/www/$app +test ! -e "$final_path" || ynh_die --message="This path already contains a folder" + +# Register (book) web path +ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_script_progression --message="Storing installation settings..." --weight=1 + +ynh_app_setting_set --app=$app --key=domain --value=$domain +ynh_app_setting_set --app=$app --key=path --value=$path_url + +#================================================= +# INSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Installing dependencies..." --weight=5 + +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." --weight=1 + +# Create a system user +ynh_system_user_create --username=$app --home_dir="$final_path" + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= +ynh_script_progression --message="Setting up source files..." --weight=1 + +ynh_app_setting_set --app=$app --key=final_path --value=$final_path +# Download, check integrity, uncompress and patch the source from app.src +ynh_setup_source --dest_dir="$final_path" + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring NGINX web server..." --weight=1 + +# Create a dedicated NGINX config +ynh_add_nginx_config + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring PHP-FPM..." --weight=2 + +# Create a dedicated PHP-FPM config +ynh_add_fpm_config + +#================================================= +# CREATE DATA DIRECTORY +#================================================= +ynh_script_progression --message="Creating a data directory..." --weight=1 + +datadir=/home/yunohost.app/$app +ynh_app_setting_set --app=$app --key=datadir --value=$datadir + +mkdir -p $datadir + +chmod 750 "$datadir" +chmod -R o-rwx "$datadir" +chown -R $app:www-data "$datadir" + +#================================================= +# ADD A CONFIGURATION +#================================================= +ynh_script_progression --message="Adding a configuration file..." --weight=1 + +ynh_add_config --template="../conf/config.ini" --destination="$final_path/config/config.ini" + +chmod 400 "$final_path/config/config.ini" +chown $app:$app "$final_path/config/config.ini" + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring permissions..." --weight=1 + +# Make app public if necessary +if [ $is_public -eq 1 ] +then + # Everyone can access the app. + # The "main" permission is automatically created before the install script. + ynh_permission_update --permission="main" --add="visitors" +fi + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Installation of $app completed" --last diff --git a/scripts/remove b/scripts/remove new file mode 100755 index 0000000..76aaf02 --- /dev/null +++ b/scripts/remove @@ -0,0 +1,80 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --weight=1 + +app=$YNH_APP_INSTANCE_NAME + +domain=$(ynh_app_setting_get --app=$app --key=domain) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +datadir=$(ynh_app_setting_get --app=$app --key=datadir) + +#================================================= +# REMOVE APP MAIN DIR +#================================================= +ynh_script_progression --message="Removing app main directory..." --weight=1 + +# Remove the app directory securely +ynh_secure_remove --file="$final_path" + +#================================================= +# REMOVE DATA DIR +#================================================= + +# Remove the data directory if --purge option is used +if [ "${YNH_APP_PURGE:-0}" -eq 1 ] +then + ynh_script_progression --message="Removing app data directory..." --weight=1 + ynh_secure_remove --file="$datadir" +fi + +#================================================= +# REMOVE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Removing NGINX web server configuration..." --weight=1 + +# Remove the dedicated NGINX config +ynh_remove_nginx_config + +#================================================= +# REMOVE PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Removing PHP-FPM configuration..." --weight=1 + +# Remove the dedicated PHP-FPM config +ynh_remove_fpm_config + +#================================================= +# REMOVE DEPENDENCIES +#================================================= +ynh_script_progression --message="Removing dependencies..." --weight=9 + +# Remove metapackage and its dependencies +ynh_remove_app_dependencies + +#================================================= +# GENERIC FINALIZATION +#================================================= +# REMOVE DEDICATED USER +#================================================= +ynh_script_progression --message="Removing the dedicated system user..." --weight=1 + +# Delete a system user +ynh_system_user_delete --username=$app + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Removal of $app completed" --last diff --git a/scripts/restore b/scripts/restore new file mode 100755 index 0000000..ba9c446 --- /dev/null +++ b/scripts/restore @@ -0,0 +1,117 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +# Keep this path for calling _common.sh inside the execution's context of backup and restore scripts +source ../settings/scripts/_common.sh +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +ynh_clean_setup () { + #### Remove this function if there's nothing to clean before calling the remove script. + true +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --time --weight=1 + +app=$YNH_APP_INSTANCE_NAME + +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) +datadir=$(ynh_app_setting_get --app=$app --key=datadir) + +#================================================= +# CHECK IF THE APP CAN BE RESTORED +#================================================= +ynh_script_progression --message="Validating restoration parameters..." --time --weight=1 + +test ! -d $final_path \ + || ynh_die --message="There is already a directory: $final_path " + +#================================================= +# STANDARD RESTORATION STEPS +#================================================= +# RESTORE THE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the NGINX web server configuration..." --time --weight=1 + +ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# RECREATE THE DEDICATED USER +#================================================= +ynh_script_progression --message="Recreating the dedicated system user..." --time --weight=1 + +# Create the dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir="$final_path" + +#================================================= +# RESTORE THE APP MAIN DIR +#================================================= +ynh_script_progression --message="Restoring the app main directory..." --time --weight=1 + +ynh_restore_file --origin_path="$final_path" + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" + +#================================================= +# RESTORE THE DATA DIRECTORY +#================================================= +ynh_script_progression --message="Restoring the data directory..." --time --weight=1 + +ynh_restore_file --origin_path="$datadir" --not_mandatory + +mkdir -p $datadir + +chmod 750 "$datadir" +chmod -R o-rwx "$datadir" +chown -R $app:www-data "$datadir" + +#================================================= +# RESTORE THE PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the PHP-FPM configuration..." --time --weight=1 + +ynh_restore_file --origin_path="/etc/php/$phpversion/fpm/pool.d/$app.conf" + +#================================================= +# SPECIFIC RESTORATION +#================================================= +# REINSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Reinstalling dependencies..." --time --weight=1 + +# Define and install dependencies +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# GENERIC FINALIZATION +#================================================= +# RELOAD NGINX AND PHP-FPM +#================================================= +ynh_script_progression --message="Reloading NGINX web server and PHP-FPM..." --time --weight=1 + +ynh_systemd_action --service_name=php$phpversion-fpm --action=reload +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Restoration completed for $app" --time --last diff --git a/scripts/upgrade b/scripts/upgrade new file mode 100644 index 0000000..22d72b1 --- /dev/null +++ b/scripts/upgrade @@ -0,0 +1,110 @@ +#!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --time --weight=1 + +app=$YNH_APP_INSTANCE_NAME + +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) + +#================================================= +# CHECK VERSION +#================================================= + +upgrade_type=$(ynh_check_app_version_changed) + +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." --time --weight=1 + +# Backup the current version of the app +ynh_backup_before_upgrade +ynh_clean_setup () { + # Restore it if the upgrade fails + ynh_restore_upgradebackup +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# STANDARD UPGRADE STEPS +#================================================= +# STOP SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Stopping a systemd service..." --time --weight=1 + +ynh_systemd_action --service_name=$app --action="stop" --log_path="/var/log/$app/$app.log" + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Making sure dedicated system user exists..." --time --weight=1 + +# Create a dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir="$final_path" + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= + +if [ "$upgrade_type" == "UPGRADE_APP" ] +then + ynh_script_progression --message="Upgrading source files..." --time --weight=1 + + # Download, check integrity, uncompress and patch the source from app.src + ynh_setup_source --dest_dir="$final_path" --keep="$final_path/config/config.ini" +fi + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading NGINX web server configuration..." --time --weight=1 + +# Create a dedicated NGINX config +ynh_add_nginx_config + +#================================================= +# UPGRADE DEPENDENCIES +#================================================= +ynh_script_progression --message="Upgrading dependencies..." --time --weight=1 + +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# PHP-FPM CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading PHP-FPM configuration..." --time --weight=1 + +# Create a dedicated PHP-FPM config +ynh_add_fpm_config + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --time --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Upgrade of $app completed" --time --last diff --git a/sources/extra_files/app/.gitignore b/sources/extra_files/app/.gitignore new file mode 100644 index 0000000..783a4ae --- /dev/null +++ b/sources/extra_files/app/.gitignore @@ -0,0 +1,2 @@ +*~ +*.sw[op] diff --git a/sources/patches/.gitignore b/sources/patches/.gitignore new file mode 100644 index 0000000..783a4ae --- /dev/null +++ b/sources/patches/.gitignore @@ -0,0 +1,2 @@ +*~ +*.sw[op]