diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2bcee80a --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# General +.DS_Store +.AppleDouble +.LSOverride +.idea + +runtime + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b789d1f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {fullname} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see [http://www.gnu.org/licenses/]. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Lsky Pro Copyright (C) 2018 熊二哈 + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +[http://www.gnu.org/licenses/]. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +[http://www.gnu.org/philosophy/why-not-lgpl.html]. \ No newline at end of file diff --git a/README.md b/README.md index 28a6e702..4e0c2e14 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,68 @@ -# lsky-pro -Lsky Pro,你的云上相册 +

+ + + +

+

Lsky Pro - Your photo album on the cloud.

+ +#### 简介: +--- +Lsky Pro,为个人站长、开发者、写博文爱好者开发的图床程序。同时可用作网络云相册。 + +#### 主要特性 +--- +- 支持第三方云储存,支持本地、阿里云OSS、腾讯云COS、七牛云、又拍云。 +- 支持多图上传、拖拽上传、上传预览、全屏预览、页面响应式布局。 +- 简洁的图片管理功能,支持鼠标右键、单选多选等操作。 +- 强大的图片预览功能,支持响应式。 +- 支持全局配置用户初始剩余储存空间、支持单个设置用户剩余储存空间。 +- 支持一键复制图片外链、二维码扫描链接。 + +#### 安装需求: +--- + +* PHP版本 ≥ 5.6 +* Mysqli +* Curl拓展 +* Zip拓展 +* Rewrite + +--- + +1. 下载兰空,上传至web运行环境,解压。 +2. 设置运行目录为 public。 +3. 配置Rewrite规则: + ##### Nginx: +
+    location / {
+        if (!-e $request_filename) {
+        	rewrite ^(.*)$ /index.php?s=$1 last; break;
+    	}
+    }
+    
+ + ##### Apache: + Apache直接使用.htaccess即可 + +4. 访问首页,未安装自动跳转至安装页面,根据页面提示安装即可。 +5. 安装完成以后请设置runtime目录0777权限,如果你使用本地存储,public 目录也需要设置为0777权限 + +#### 联系我 +--- +QQ:1591788658
+Email: 1591788658@qq.com
+Blog: www.wispx.cn + +#### 鸣谢 +- ThinkPHP +- Jquery +- BootStrap +- Mdui +- viewer.js +- context.js + +#### 开源许可 +--- +GPL 3.0 + +Copyright (c) 2017 Wisp X. diff --git a/application/.htaccess b/application/.htaccess new file mode 100644 index 00000000..3418e55a --- /dev/null +++ b/application/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/application/command.php b/application/command.php new file mode 100644 index 00000000..826bb2b2 --- /dev/null +++ b/application/command.php @@ -0,0 +1,12 @@ + +// +---------------------------------------------------------------------- + +return []; diff --git a/application/common.php b/application/common.php new file mode 100644 index 00000000..86f16959 --- /dev/null +++ b/application/common.php @@ -0,0 +1,111 @@ + +// +---------------------------------------------------------------------- + +// 应用公共文件 + +/** + * 转换文件大小单位 + * + * @param $size + * + * @return string + */ +function format_size($size) +{ + if (0 == $size) { + return "0.00 Bytes"; + } + $unit = ['','K','M','G','T','P']; + $base = 1024; + $i = floor(log($size, $base)); + $n = count($unit); + if($i >= $n) { + $i = $n - 1; + } + + return sprintf("%.2f", $size / pow($base, $i)) . ' ' . $unit[$i] . 'B'; +} + +/** + * 过滤逗号(去除字符串两边的逗号,并将中文逗号转换成英文逗号) + * + * @param $str 字符串 + * + * @return string + */ +function filter_comma($str) +{ + return trim(str_replace(",", ",", $str), ","); +} + +/** + * 获取文件后缀 + * + * @param $name 文件名 + * + * @return mixed + */ +function get_file_ext($name) +{ + return pathinfo($name, PATHINFO_EXTENSION); +} + +/** + * 随机字符串 + * + * @param int $length + * @param string $char + * @return bool|string + */ +function str_rand($length = 16, $char = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') { + $string = ''; + for($i = $length; $i > 0; $i--) { + $string .= $char[mt_rand(0, strlen($char) - 1)]; + } + + return $string; +} + +/** + * Make Token + * + * @return string + */ +function make_token() +{ + return md5('LSKY PRO' .time()); +} + +/** + * Make Url + * + * @param $domain + * @param null $pathname + * @return mixed + */ +function make_url($domain, $pathname = null) +{ + $domain = rtrim($domain, '/'); + if (false === strpos($domain, 'https://') && false === strpos($domain, 'http://')) { + $domain = 'http://' . $domain; + } + return str_replace('\\', '/', $domain . ($pathname ? '/' . $pathname : null)); +} + +/** + * @param int $length + * @return int + */ +function generate_code($length = 5) { + $min = pow(10 , ($length - 1)); + $max = pow(10, $length) - 1; + return rand($min, $max); +} \ No newline at end of file diff --git a/application/common/model/Config.php b/application/common/model/Config.php new file mode 100644 index 00000000..f55a51f2 --- /dev/null +++ b/application/common/model/Config.php @@ -0,0 +1,18 @@ + $data['strategy'], + 'name' => $data['strategy'] . '_cdn_domain', + ])->value('value'); + $domain = $cdnDoamin ? $cdnDoamin : request()->domain(); + return make_url($domain, $data['pathname']); + } +} \ No newline at end of file diff --git a/application/common/model/Users.php b/application/common/model/Users.php new file mode 100644 index 00000000..84666408 --- /dev/null +++ b/application/common/model/Users.php @@ -0,0 +1,75 @@ +ip(); + } + + public function setQuotaAttr() + { + return Config::where('name', 'user_initial_quota')->value('value'); + } + + public function getUseQuotaAttr() + { + return $this->hasMany('Images', 'user_id', 'id')->sum('size'); + } + + public static function login($account, $password) + { + $field = 'email'; + + if (!$account) { + throw new Exception('请输入账号'); + } + + if (!$password) { + throw new Exception('请输入密码'); + } + + if ($user = self::get([$field => $account])) { + if (0 === $user->state) { + throw new Exception('你的账户已被冻结,请联系管理员!'); + } + if ($user->password !== md5($password)) { + throw new Exception('密码不正确'); + } + $token = make_token(); + Session::set('uid', $user->id); + Session::set('token', $token); + $user->token = $token; + $user->save(); + } else { + throw new Exception('用户不存在'); + } + } + + public function images() + { + return $this->hasMany('Images', 'user_id', 'id'); + } +} \ No newline at end of file diff --git a/application/common/validate/Users.php b/application/common/validate/Users.php new file mode 100644 index 00000000..aff76a9e --- /dev/null +++ b/application/common/validate/Users.php @@ -0,0 +1,52 @@ + 'require|max:15|unique:users', + 'nickname' => 'max:15', + 'email' => 'require|email|max:50|unique:users', + 'password' => 'require|confirm', + 'captcha' => 'require|captcha|token', + ]; + + protected $message = [ + 'username.require' => '用户名不能为空', + 'username.max' => '用户名字符长度超出', + 'username.unique' => '用户名已存在,请更换', + 'nickname.max' => '昵称字符长度超出', + 'email.require' => '邮箱不能为空', + 'email.email' => '邮箱格式不正确', + 'email.max' => '邮箱字符长度超出', + 'email.unique' => '邮箱已存在', + 'password.require' => '密码不能为空', + 'password.confirm' => '两次输入的密码不一致', + 'captcha.require' => '请输入验证码', + 'captcha.captcha' => '验证码错误', + ]; + + public function sceneEdit() + { + return $this->only(['nickname', 'password'])->remove('password', 'require'); + } + + public function sceneAdminEdit() + { + return $this->only(['username', 'nickname', 'email', 'password'])->remove('password', 'require'); + } + + public function sceneInstall() + { + return $this->only(['username', 'email', 'password']); + } +} \ No newline at end of file diff --git a/application/http/middleware/Auth.php b/application/http/middleware/Auth.php new file mode 100644 index 00000000..28f40d73 --- /dev/null +++ b/application/http/middleware/Auth.php @@ -0,0 +1,38 @@ +controller() . '/' . $request->action()); + if (!in_array($uri, array_map('strtolower', $this->noNeedLogin))) { + if (!Session::has('uid') || !Session::has('token')) { + return redirect(url('auth/login')); + } + /*if (!Users::get(['id' => Session::get('uid'), 'token' => Session::get('token')])) { + Session::delete(['uid', 'token']); + return redirect(url('auth/login')); + }*/ + } + + return $next($request); + } +} diff --git a/application/index/config/naming.php b/application/index/config/naming.php new file mode 100644 index 00000000..b287a436 --- /dev/null +++ b/application/index/config/naming.php @@ -0,0 +1,178 @@ + [ + [ + 'name' => '{Y}', + 'example' => '2018', + 'explain' => '年', + 'value' => date('Y'), + ], + [ + 'name' => '{m}', + 'example' => '01', + 'explain' => '月', + 'value' => date('m'), + ], + [ + 'name' => '{d}', + 'example' => '04', + 'explain' => '日', + 'value' => date('d'), + ], + [ + 'name' => '{Ymd}', + 'example' => '20180104', + 'explain' => '上传日期', + 'value' => date('Ymd'), + ], + [ + 'name' => '{ymd}', + 'example' => '180104', + 'explain' => '上传日期', + 'value' => date('ymd'), + ], + [ + 'name' => '{Y-m-d}', + 'example' => '2018-01-04', + 'explain' => '上传日期', + 'value' => date('Y-m-d'), + ], + [ + 'name' => '{y-m-d}', + 'example' => '18-01-04', + 'explain' => '上传日期', + 'value' => date('y-m-d'), + ], + [ + 'name' => '{time-stamp}', + 'example' => '1514995200', + 'explain' => '上传日期', + 'value' => $time, + ], + [ + 'name' => '{uniqid}', + 'example' => '5bb2f89a38935', + 'explain' => '唯一ID', + 'value' => uniqid(), + ], + [ + 'name' => '{md5-32}', + 'example' => 'f96087bb0a9a5e8723dbde0d0f8dce34', + 'explain' => 'md5(非文件md5)', + 'value' => $md5, + ], + [ + 'name' => '{md5-16}', + 'example' => '0a9a5e8723dbde0d', + 'explain' => 'md5(非文件md5)', + 'value' => substr($md5, 8, 16), + ], + [ + 'name' => '{uid}', + 'example' => '1', + 'explain' => '用户ID(用户已登录可用,否则为0)', + 'value' => $uid, + ], + [ + 'name' => '{rend-character}', + 'example' => 'HgTLmGcDceplMduF', + 'explain' => '16位随机字符串', + 'value' => str_rand(), + ], + ], + 'file' => [ + [ + 'name' => '{Y}', + 'example' => '2018', + 'explain' => '年', + 'value' => date('Y'), + ], + [ + 'name' => '{m}', + 'example' => '01', + 'explain' => '月', + 'value' => date('m'), + ], + [ + 'name' => '{d}', + 'example' => '04', + 'explain' => '日', + 'value' => date('d'), + ], + [ + 'name' => '{Ymd}', + 'example' => '20180104', + 'explain' => '上传日期', + 'value' => date('Ymd'), + ], + [ + 'name' => '{ymd}', + 'example' => '180104', + 'explain' => '上传日期', + 'value' => date('ymd'), + ], + [ + 'name' => '{Y-m-d}', + 'example' => '2018-01-04', + 'explain' => '上传日期', + 'value' => date('Y-m-d'), + ], + [ + 'name' => '{y-m-d}', + 'example' => '18-01-04', + 'explain' => '上传日期', + 'value' => date('y-m-d'), + ], + [ + 'name' => '{time-stamp}', + 'example' => '1514995200', + 'explain' => '上传日期', + 'value' => $time, + ], + [ + 'name' => '{uniqid}', + 'example' => '5bb2f89a38935', + 'explain' => '唯一ID', + 'value' => uniqid(), + ], + [ + 'name' => '{md5-32}', + 'example' => 'f96087bb0a9a5e8723dbde0d0f8dce34', + 'explain' => 'md5(非文件md5)', + 'value' => $md5, + ], + [ + 'name' => '{md5-16}', + 'example' => '0a9a5e8723dbde0d', + 'explain' => 'md5(非文件md5)', + 'value' => substr($md5, 8, 16), + ], + [ + 'name' => '{uid}', + 'example' => '1', + 'explain' => '用户ID(用户已登录可用,否则为0)', + 'value' => $uid, + ], + [ + 'name' => '{rend-character}', + 'example' => 'HgTLmGcDceplMduF', + 'explain' => '16位随机字符串', + 'value' => str_rand(), + ], + ], +]; \ No newline at end of file diff --git a/application/index/config/strategy.php b/application/index/config/strategy.php new file mode 100644 index 00000000..bdd6071d --- /dev/null +++ b/application/index/config/strategy.php @@ -0,0 +1,32 @@ + [ + 'name' => '本地', + 'class' => \strategy\driver\Local::class + ], + 'oss' => [ + 'name' => '阿里云OSS', + 'class' => \strategy\driver\Oss::class + ], + 'cos' => [ + 'name' => '腾讯云COS', + 'class' => \strategy\driver\Cos::class + ], + 'qiniu' => [ + 'name' => '七牛云', + 'class' => \strategy\driver\Qiniu::class + ], + 'upyun' => [ + 'name' => '又拍云', + 'class' => \strategy\driver\Upyun::class + ], +]; \ No newline at end of file diff --git a/application/index/controller/Api.php b/application/index/controller/Api.php new file mode 100644 index 00000000..aeb1f4cb --- /dev/null +++ b/application/index/controller/Api.php @@ -0,0 +1,145 @@ +request->isPost()) { + Db::startTrans(); + try { + if (!$this->config['allowed_tourist_upload'] && !$this->user) { + throw new Exception('管理员关闭了游客上传,请先登录账号', 401); + } + + $image = $this->getImage(); + $size = $image->getSize(); + $mime = $image->getMime(); + $sha1 = $image->hash('sha1'); + $md5 = $image->hash('md5'); + + if ($this->user) { + if (($this->user->use_quota + $size) > $this->user->quota) { + throw new Exception('保存失败!您的储存容量不足,请联系管理员!', 0); + } + } + + // 当前储存策略 + $currentStrategy = strtolower($this->config['storage_strategy']); + // 获取当前储存策略配置 + $strategyConfig = $this->currentStrategyConfig; + // 获取当前驱动实例 + $strategy = $this->getStrategyInstance(); + + $pathname = $this->makePathname($image->getInfo('name')); + if (!$strategy->create($pathname, $image->getPathname())) { + if (Config::get('app.app_debug')) { + throw new Exception($strategy->getError(), 500); + } + throw new Exception('上传失败', 500); + } + + $cdnDomain = $currentStrategy . '_cdn_domain'; + $domain = $this->request->domain(); + if (array_key_exists($cdnDomain, $strategyConfig)) { + if ($strategyConfig[$cdnDomain]) { + $domain = $strategyConfig[$cdnDomain]; + } + } + + if (!Images::create([ + 'user_id' => $this->user ? $this->user->id : 0, + 'strategy' => $currentStrategy, + 'path' => dirname($pathname), + 'name' => basename($pathname), + 'pathname' => $pathname, + 'size' => $size, + 'mime' => $mime, + 'sha1' => $sha1, + 'md5' => $md5 + ])) { + $strategy->delete($pathname); + throw new Exception('图片数据保存失败', 500); + } + + $data = [ + 'name' => $image->getInfo('name'), + 'url' => make_url($domain, $pathname), + ]; + if ($this->user) { + $data['quota'] = sprintf('%.2f', (float)$this->user->quota); + $data['use_quota'] = sprintf('%.2f', (float)$this->user->use_quota); + } + + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + return $this->result(null, $e->getCode(), $e->getMessage()); + } + + return $this->result($data, 200, '上传成功'); + } + } + + /** + * 获取图片资源 + * + * @return array|null|\think\File + * @throws Exception + */ + private function getImage() + { + $image = $this->request->file('image'); + if (null === $image) { + throw new Exception('图片资源获取失败!'); + } + if (!is_uploaded_file($image->getPathname())) { + throw new Exception('file is not uploaded via HTTP POST'); + } + if (!$image->check([ + 'size' => $this->config['upload_max_size'], + 'ext' => filter_comma($this->config['upload_allowed_exts']), + ])) { + throw new Exception($image->getError()); + } + return $image; + } + + /** + * Make pathname + * + * @param $name 文件名 + * + * @return string + */ + private function makePathname($name) + { + $naming = Config::pull('naming'); + + $path = trim(str_replace( + array_column($naming['path'], 'name'), + array_column($naming['path'], 'value'), + $this->config['path_naming_rule'] + ), '/'); + + $file = trim(str_replace( + array_column($naming['file'], 'name'), + array_column($naming['file'], 'value'), + $this->config['file_naming_rule'] + ), '/') . '.' . get_file_ext($name); + + return $path . '/' . $file; + } +} \ No newline at end of file diff --git a/application/index/controller/Auth.php b/application/index/controller/Auth.php new file mode 100644 index 00000000..b934ddd4 --- /dev/null +++ b/application/index/controller/Auth.php @@ -0,0 +1,115 @@ +request->isPost()) { + try { + Users::login($account, $password); + } catch (Exception $e) { + Session::flash('error', $e->getMessage()); + return $this->fetch(); + } + $this->redirect(url('/')); + } + return $this->fetch(); + } + + public function register() + { + if ($this->request->isPost()) { + try { + if ($this->config['close_register']) { + throw new Exception('站点已关闭注册'); + } + $data = $this->request->post(); + $validate = $this->validate($data, 'Users'); + if (true !== $validate) { + throw new Exception($validate); + } + Users::create($data); + } catch (Exception $e) { + Session::flash('error', $e->getMessage()); + return $this->fetch(); + } + Session::flash('success', '注册成功'); + $this->redirect(url('auth/login')); + } + return $this->fetch(); + } + + public function forgot() + { + $delSession = function () { + Session::delete('code', 'forgot_'); + Session::delete('email', 'forgot_'); + }; + if ($this->request->isPost()) { + try { + $data = $this->request->post(); + $validate = $this->validate($data, [ + 'password|密码' => 'require|confirm', + ]); + if (true !== $validate) { + return $this->error($validate); + } + if ($data['code'] != Session::get('code', 'forgot_')) { + throw new Exception('验证码不正确'); + } + if (!$user = Users::get(['email' => Session::get('email', 'forgot_')])) { + throw new Exception('用户不存在'); + } + $user->password = $data['password']; + $user->save(); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + $delSession(); + return $this->success('重置成功'); + } + $delSession(); + return $this->fetch(); + } + + public function sendCode() + { + if ($this->request->isPost()) { + $data = $this->request->post(); + $validate = $this->validate($data, [ + 'email|邮箱' => 'require|email', + 'captcha|验证码' => 'require|captcha' + ]); + if (true !== $validate) { + return $this->error($validate); + } + + if (!$user = Users::get(['email' => $data['email']])) { + return $this->error('账号不存在'); + } + + $code = generate_code(); + $err = $this->sendMail($data['email'], '找回密码', "尊敬的 {$user->username}, 您好,您正在申请重置密码操作,本次的验证码是 {$code},如果不是您本人操作请忽略!"); + + if (true !== $err) { + return $this->error($err); + } + + Session::set('code', $code, 'forgot_'); + Session::set('email', $data['email'], 'forgot_'); + return $this->success('发送成功'); + } + } +} \ No newline at end of file diff --git a/application/index/controller/Base.php b/application/index/controller/Base.php new file mode 100644 index 00000000..d4001086 --- /dev/null +++ b/application/index/controller/Base.php @@ -0,0 +1,133 @@ +redirect(url('/install')); + } + + $configs = \app\common\model\Config::all(); + foreach ($configs as $key => &$value) { + $this->config[$value->name] = $value->value; + } + $this->configs = $configs; + if (Session::has('uid') && Session::has('token')) { + $this->user = Users::get([ + 'id' => Session::get('uid'), + 'token' => Session::get('token') + ]); + if (!$this->user) { + Session::delete(['uid', 'token']); + } + } + + $this->currentStrategyConfig = $this->getStrategyConfig(strtolower($this->config['storage_strategy'])); + + $this->assign([ + 'config' => $this->config, + 'user' => $this->user, + 'uri' => strtolower($this->request->controller() . '/' . $this->request->action()) + ]); + } + + /** + * 当前储存策略配置 + * + * @param $strategy + * @return array + */ + public function getStrategyConfig($strategy) + { + $strategyConfig = []; + foreach ($this->configs as $value) { + if ($value->key === $strategy) { + $strategyConfig[$value->name] = $value->value; + } + } + + return $strategyConfig; + } + + /** + * 获取储存策略驱动实例 + * + * @param null $strategy + * @return mixed + */ + public function getStrategyInstance($strategy = null) + { + $currentStrategy = $strategy ? $strategy : strtolower($this->config['storage_strategy']); + // 驱动 + $driver = Config::get('strategy.' . $currentStrategy . '.class'); + // 获取该储存策略配置 + $strategyConfig = $this->getStrategyConfig($currentStrategy); + + return new $driver($strategyConfig); + } + + public function sendMail($email, $subject, $body) + { + $mail = new PHPMailer(); + try { + // $mail->SMTPDebug = 2; + if ('smtp' === $this->config['mail_send_mode']) { + $mail->isSMTP(); + } else { + $mail->isMail(); + } + $mail->Host = $this->config['mail_smtp_host']; + $mail->CharSet = 'UTF-8'; + $mail->SMTPAuth = true; + $mail->Username = $this->config['mail_smtp_username']; + $mail->Password = $this->config['mail_smtp_password']; + $mail->SMTPSecure = $this->config['mail_smtp_secure']; + $mail->Port = $this->config['mail_smtp_port']; + $mail->setFrom($this->config['mail_form_email'], $this->config['site_name']); + $mail->addAddress($email); + $mail->addReplyTo($this->config['mail_form_email'], $this->config['site_name']); + $mail->isHTML(true); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->send(); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + + return true; + } +} \ No newline at end of file diff --git a/application/index/controller/Index.php b/application/index/controller/Index.php new file mode 100644 index 00000000..0c4bab81 --- /dev/null +++ b/application/index/controller/Index.php @@ -0,0 +1,17 @@ +fetch(); + } +} diff --git a/application/index/controller/Install.php b/application/index/controller/Install.php new file mode 100644 index 00000000..50783ee9 --- /dev/null +++ b/application/index/controller/Install.php @@ -0,0 +1,121 @@ += 5.6; + $isCurl = function_exists('curl_init'); + $isZip = function_exists('zip_open'); + $isMysqli = class_exists('mysqli'); + + switch ($stop) { + case 1: + // 运行环境检测 + $testing = $phpVerGt56 && $isCurl && $isZip && $isMysqli; + $this->assign([ + 'phpVerGt56' => $phpVerGt56, + 'isCurl' => $isCurl, + 'isZip' => $isZip, + 'isMysqli' => $isMysqli, + 'testing' => $testing + ]); + break; + case 2: + // 安装 + if ($this->request->isPost()) { + $hostname = $this->request->post('hostname'); + $database = $this->request->post('database'); + $username = $this->request->post('username'); + $password = $this->request->post('password'); + $hostport = $this->request->post('hostport'); + try { + if (!$sqlFile = file_get_contents($rootPath . 'install.sql')) { + throw new Exception('安装文件不存在'); + } + $mysqli = new \mysqli($hostname, $username, $password, $database, $hostport); + if ($mysqli->connect_error) { + $mysqli->close(); + throw new Exception($mysqli->connect_error); + } + if (!$mysqli->multi_query($sqlFile)) { + throw new Exception('数据写入失败'); + } + $dataBasePath = $configPath . 'database.php'; + $dataBaseFile = file_get_contents($dataBasePath); + $dataBaseFile = str_replace([ + '{hostname}', + '{database}', + '{username}', + '{password}', + '{hostport}', + ], [ + $hostname, + $database, + $username, + $password, + $hostport, + ], $dataBaseFile); + + file_put_contents($dataBasePath, $dataBaseFile); + + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + return $this->success('数据写入成功'); + } + break; + case 3: + // 设置管理员账号密码 + if ($this->request->isPost()) { + try { + $data = $this->request->post(); + $validate = $this->validate($data, 'Users.Install'); + if (true !== $validate) { + throw new Exception($validate); + } + $data['is_admin'] = 1; + $data['quota'] = 1073741824; + Users::create($data); + fopen($rootPath . 'install.lock', 'w'); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + Session::flash('install_success', true); + // 删除sql文件 + @unlink($rootPath . 'install.sql'); + return $this->success('设置成功'); + } + break; + } + + $this->assign([ + 'stop' => $stop + ]); + return $this->fetch(); + } +} \ No newline at end of file diff --git a/application/index/controller/User.php b/application/index/controller/User.php new file mode 100644 index 00000000..c3d0c3cc --- /dev/null +++ b/application/index/controller/User.php @@ -0,0 +1,114 @@ +request->isPost()) { + try { + $model = $this->user->images()->order('create_time', 'desc'); + if (!empty($keyword)) { + $model = $model->where('pathname', 'like', "%{$keyword}%"); + } + $images = $model->paginate($limit)->each(function ($item) { + $item->url = $item->url; + // TODO 生成缩略图 + return $item; + }); + } catch (Exception $e) { + exit(dump($e->getMessage())); + } + return $this->success('success', null, $images); + } + return $this->fetch(); + } + + public function deleteImages() + { + if ($this->request->isPost()) { + Db::startTrans(); + try { + $id = $this->request->post('id'); + $deletes = []; // 需要删除的文件 + if (is_array($id)) { + $images = Images::all($id); + foreach ($images as &$value) { + $deletes[$value->strategy][] = $value->pathname; + $value->delete(); + unset($value); + } + } else { + $image = Images::get($id); + if (!$image) { + throw new Exception('没有找到该图片数据'); + } + $deletes[$image->strategy][] = $image->pathname; + $image->delete(); + } + // 是否开启软删除(开启了只删除记录,不删除文件) + if (!$this->config['soft_delete']) { + $strategy = []; + // 实例化所有储存策略驱动 + $strategyAll = array_keys(Config::pull('strategy')); + foreach ($strategyAll as $value) { + // 获取储存策略驱动 + $strategy[$value] = $this->getStrategyInstance($value); + } + + foreach ($deletes as $key => $val) { + $strategy[$key]->deletes($val); + } + } + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + return $this->error($e->getMessage()); + } + return $this->success('删除成功'); + } + } + + public function settings() + { + if ($this->request->isPost()) { + try { + $data = $this->request->post(); + $validate = $this->validate($data, 'Users.edit'); + if (true !== $validate) { + throw new Exception($validate); + } + if ($data['password_old']) { + if (md5($data['password_old']) != $this->user->password) { + throw new Exception('原密码不正确'); + } + } + if (!$data['password']) unset($data['password']); + $this->user->save($data); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + return $this->success('保存成功'); + } + return $this->fetch(); + } + + public function logout() + { + Session::delete('uid'); + $this->redirect(url('/')); + } +} \ No newline at end of file diff --git a/application/index/controller/admin/Base.php b/application/index/controller/admin/Base.php new file mode 100644 index 00000000..55e27a40 --- /dev/null +++ b/application/index/controller/admin/Base.php @@ -0,0 +1,36 @@ +user) { + $this->redirect(url('auth/login')); + } + if (!$this->user->is_admin) { + $this->redirect(url('/')); + } + } + + protected function getConfigs($keys) + { + $configs = []; + foreach ($this->configs as &$value) { + if (in_array($value->key, $keys)) { + $configs[$value->key][] = $value; + } + } + return $configs; + } +} \ No newline at end of file diff --git a/application/index/controller/admin/Images.php b/application/index/controller/admin/Images.php new file mode 100644 index 00000000..76c9fb5a --- /dev/null +++ b/application/index/controller/admin/Images.php @@ -0,0 +1,105 @@ +strategyList = Config::pull('strategy'); + $this->assign('strategyList', $this->strategyList); + } + + public function index($strategy = '', $keyword = '', $limit = 15) + { + $model = new ImagesModel(); + if (!empty($strategy)) { + $model = $model->where('strategy', $strategy); + } + if (!empty($keyword)) { + $model = $model->where('pathname|sha1|md5', 'like', "%{$keyword}%"); + } + $images = $model->order('id', 'desc')->paginate($limit, false, [ + 'query' => [ + 'keyword' => $keyword + ] + ])->each(function ($item) { + $item->username = Users::where('id', $item->user_id)->value('username'); + $item->strategy = $this->strategyList[$item->strategy]['name']; + return $item; + }); + $this->assign([ + 'images' => $images, + 'keyword' => $keyword, + 'strategyList' => $this->strategyList, + 'strategy' => $strategy + ]); + return $this->fetch(); + } + + public function delete() + { + if ($this->request->isPost()) { + Db::startTrans(); + try { + $id = $this->request->post('id'); + $deletes = []; // 需要删除的文件 + if (is_array($id)) { + $images = ImagesModel::all($id); + foreach ($images as &$value) { + $deletes[$value->strategy][] = $value->pathname; + $value->delete(); + unset($value); + } + } else { + $image = ImagesModel::get($id); + if (!$image) { + throw new Exception('没有找到该图片数据'); + } + $deletes[$image->strategy][] = $image->pathname; + $image->delete(); + } + // 是否开启软删除(开启了只删除记录,不删除文件) + if (!$this->config['soft_delete']) { + $strategy = []; + // 实例化所有储存策略驱动 + $strategyAll = array_keys($this->strategyList); + foreach ($strategyAll as $value) { + // 获取储存策略驱动 + $strategy[$value] = $this->getStrategyInstance($value); + } + + foreach ($deletes as $key => $val) { + $strategy[$key]->deletes($val); + } + } + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + return $this->error($e->getMessage()); + } + return $this->success('删除成功'); + } + } +} \ No newline at end of file diff --git a/application/index/controller/admin/Strategy.php b/application/index/controller/admin/Strategy.php new file mode 100644 index 00000000..bc9d4316 --- /dev/null +++ b/application/index/controller/admin/Strategy.php @@ -0,0 +1,51 @@ +assign([ + 'configs' => parent::getConfigs(array_keys($strategy)), + 'strategy' => $strategy + ]); + } + + public function index() + { + if ($this->request->isPost()) { + Db::startTrans(); + try { + $data = $this->request->post(); + foreach ($data as $key => $value) { + Config::where('name', $key)->setField('value', $value); + } + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + return $this->error($e->getMessage()); + } + return $this->success('保存成功'); + } + return $this->fetch(); + } +} \ No newline at end of file diff --git a/application/index/controller/admin/System.php b/application/index/controller/admin/System.php new file mode 100644 index 00000000..d0c141a3 --- /dev/null +++ b/application/index/controller/admin/System.php @@ -0,0 +1,62 @@ +assign('configs', parent::getConfigs(['basics', 'upload', 'user', 'mail', 'other'])); + } + + public function index() + { + if ($this->request->isPost()) { + Db::startTrans(); + try { + $data = $this->request->post(); + foreach ($data as $key => $value) { + Config::where('name', $key)->setField('value', $value); + } + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + return $this->error($e->getMessage()); + } + return $this->success('保存成功'); + } + // 命名规则 + $naming = \think\facade\Config::pull('naming'); + $this->assign('naming', $naming); + return $this->fetch(); + } + + public function testMail() + { + if ($this->request->isPost()) { + $email = $this->request->post('email'); + $err = $this->sendMail($email, '测试', '这是一封测试邮件!'); + if (true !== $err) { + return $this->error($err); + } + return $this->success('发送成功'); + } + } +} \ No newline at end of file diff --git a/application/index/controller/admin/Update.php b/application/index/controller/admin/Update.php new file mode 100644 index 00000000..a67f2d3c --- /dev/null +++ b/application/index/controller/admin/Update.php @@ -0,0 +1,350 @@ +record = $record; + } + $this->assign('record', $this->record); + } + + public function index() + { + // 检测是否为最新版 + $newest = json_decode($this->curl(self::VERSION_API), true); + $this->assign('newest', $newest); + return $this->fetch(); + } + + public function toUpdate() + { + if ($this->request->isPost()) { + Db::startTrans(); + try { + // 获取最新版数据 + $newest = json_decode($this->curl(self::VERSION_API)); + if (!$newest) { + throw new Exception('更新数据获取失败'); + } + // 根目录路径 + $rootPath = Env::get('root_path'); + // 缓存目录路径 + $runtimePath = Env::get('runtime_path'); + // 版本缓存目录路径 + $verPath = $runtimePath . $newest->version . DIRECTORY_SEPARATOR; + // 文件暂存路径 + $pathname = $verPath . basename($newest->link); + // 下载文件 + if (!$this->getFile($newest->link, $pathname)) { + throw new Exception('更新文件下载失败'); + } + // 解压到暂存目录 + if (!$this->unZip($pathname, $verPath)) { + @unlink($pathname); + throw new Exception('更新文件解压失败'); + } + // 是否存在更新配置文件 + $updateConfig = $verPath . 'update.php'; + if ($config = @include($updateConfig)) { + foreach ($config as $key => $value) { + switch ($key) { + // 遍历删除文件 + case 'delete': + foreach ($value as $val) { + @unlink($rootPath . trim($val, '/')); + } + break; + } + } + + // 删除配置文件 + @unlink($updateConfig); + } + // 是否存在sql文件 + $updateSql = $verPath . 'update.sql'; + if ($sql = @file_get_contents($updateSql)) { + Db::execute($sql); + + // 删除sql文件 + @unlink($updateSql); + } + // 更新版本号 + Config::where('name', 'system_version')->setField('value', $newest->version); + // 删除压缩包 + @unlink($pathname); + // 移动文件 + $this->recurseCopy($verPath, $rootPath); + // 删除更新缓存目录 + $this->delFolder($verPath); + // 写入更新记录文件 + array_unshift($this->record, $newest); + file_put_contents($rootPath . 'update.json', json_encode($this->record, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT)); + Db::commit(); + } catch (Exception $e) { + Db::rollback(); + //return $this->error($e->getLine()); + return $this->error($e->getMessage()); + } + return $this->success('更新成功'); + } + } + + private function delFolder($path) + { + if (is_dir($path)) { + // 扫描一个文件夹内的所有文件夹和文件并返回数组 + $p = scandir($path); + foreach ($p as $val) { + // 排除目录中的.和.. + if ($val != "." && $val != "..") { + // 如果是目录则递归子目录,继续操作 + if (is_dir($path . $val)) { + // 子目录中操作删除文件夹和文件 + $this->delFolder($path . $val . DIRECTORY_SEPARATOR); + // 目录清空后删除空文件夹 + @rmdir($path . $val . DIRECTORY_SEPARATOR); + } else { + // 如果是文件直接删除 + @unlink($path . $val); + } + } + } + @rmdir($path); + } + } + + /** + * 递归移动源目录(包括文件和子文件)到目的目录【或移动源文件到新文件】 + * + * @param [string] $source 源目录或源文件 + * @param [string] $target 目的目录或目的文件 + * + * @return boolean true + */ + private function moveFolder($source, $target) + { + + if (!file_exists($source)) { + return false; + } //如果源目录/文件不存在返回false + + //如果要移动文件 + if (filetype($source) == 'file') { + $basedir = dirname($target); + if (!is_dir($basedir)) { + mkdir($basedir); + } //目标目录不存在时给它创建目录 + copy($source, $target); + unlink($source); + + } else { //如果要移动目录 + + if (!file_exists($target)) { + mkdir($target); + } //目标目录不存在时就创建 + + $files = array(); //存放文件 + $dirs = array(); //存放目录 + $fh = opendir($source); + + if ($fh != false) { + while ($row = readdir($fh)) { + $src_file = $source . DIRECTORY_SEPARATOR . $row; //每个源文件 + if ($row != '.' && $row != '..') { + if (!is_dir($src_file)) { + $files[] = $row; + } else { + $dirs[] = $row; + } + } + } + closedir($fh); + } + + foreach ($files as $v) { + copy($source . DIRECTORY_SEPARATOR . $v, $target . DIRECTORY_SEPARATOR . $v); + unlink($source . DIRECTORY_SEPARATOR . $v); + } + + if (count($dirs)) { + foreach ($dirs as $v) { + $this->moveFolder($source . DIRECTORY_SEPARATOR . $v, $target . DIRECTORY_SEPARATOR . $v); + } + } + } + return true; + } + + + private function curl($url) + { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + $data = curl_exec($curl); + curl_close($curl); + return $data; + } + + /** + * 复制目录下的所有文件及文件夹 + * + * @param $src 原目录 + * @param $dst 新目录 + */ + private function recurseCopy($src, $dst) + { + $dir = opendir($src); + @mkdir($dst); + while (false !== ($file = readdir($dir))) { + if (($file != '.') && ($file != '..')) { + if (is_dir($src . DIRECTORY_SEPARATOR . $file)) { + $this->recurseCopy($src . DIRECTORY_SEPARATOR . $file, $dst . DIRECTORY_SEPARATOR . $file); + } else { + copy($src . DIRECTORY_SEPARATOR . $file, $dst . DIRECTORY_SEPARATOR . $file); + } + } + } + closedir($dir); + } + + /** + * 获取远程文件 + * + * @param $url + * @param string $pathname + * @param int $timeout + * + * @return bool|mixed|string + */ + private function getFile($url, $pathname = "", $timeout = 60) + { + $pathname = empty($pathname) ? pathinfo($url, PATHINFO_BASENAME) : $pathname; + $dir = pathinfo($pathname, PATHINFO_DIRNAME); + !is_dir($dir) && @mkdir($dir, 0755, true); + $url = str_replace(" ", "%20", $url); + + if (function_exists('curl_init')) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $temp = curl_exec($ch); + if (@file_put_contents($pathname, $temp) && !curl_error($ch)) { + return $pathname; + } else { + return false; + } + } else { + $options = [ + 'http' => [ + 'method' => 'GET', + 'header' => '', + 'timeout' => $timeout + ] + ]; + $context = stream_context_create($options); + if (@copy($url, $pathname, $context)) { + // $http_response_header + return $pathname; + } else { + return false; + } + } + } + + /** + * zip文件解压 + * + * @param $zipFile zip文件的路径,可以不加zip文件后缀.如果其他类型的文件伪装成zip解压也会失败 + * @param $dir 解压到的文件夹路径 + * @param array $extArray 允许解压的文件后缀名数组,解压任何类型的文件 + * + * @return bool 解压失败返回false,成功返回true + */ + private function unZip($zipFile, $dir, $extArray = []) + { + + if (!file_exists($zipFile)) { + return false; + } + + if (!is_dir($dir)) { + + if (!mkdir($dir, 0755, true)) { + return false; + } + } + + $resource = zip_open($zipFile); + + // 其他类型的文件伪装成zip文件时出现此种情况 + if (is_numeric($resource)) { + return false; + } + + while ($dirResource = zip_read($resource)) { + if (!zip_entry_open($resource, $dirResource)) { + continue; + } + + $fileName = str_replace(['/', '\\'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $dir . zip_entry_name($dirResource)); + $filePath = substr($fileName, 0, strrpos($fileName, DIRECTORY_SEPARATOR)); + + if (!is_dir($filePath)) { + if (!mkdir($filePath, 0755, true)) { + return false; + } + } + + if (is_dir($fileName)) { + zip_entry_close($dirResource); + continue; + } + + if (count($extArray) > 0) { + if (!in_array(pathinfo($fileName, PATHINFO_EXTENSION), $extArray)) { + continue; + } + } + + $fileSize = zip_entry_filesize($dirResource); + $fileContent = zip_entry_read($dirResource, $fileSize); + file_put_contents($fileName, $fileContent); + + zip_entry_close($dirResource); + } + + zip_close($resource); + return true; + } +} \ No newline at end of file diff --git a/application/index/controller/admin/Users.php b/application/index/controller/admin/Users.php new file mode 100644 index 00000000..302db9f3 --- /dev/null +++ b/application/index/controller/admin/Users.php @@ -0,0 +1,130 @@ +where('state', $state); + } + if (!empty($keyword)) { + $model = $model->where('username|nickname|email', 'like', "%{$keyword}%"); + } + $users = $model->order('id', 'desc')->paginate($limit, false, [ + 'query' => [ + 'state' => $state, + 'keyword' => $keyword + ] + ]); + $this->assign([ + 'users' => $users, + 'state' => $state, + 'keyword' => $keyword + ]); + return $this->fetch(); + } + + public function delete() + { + if ($this->request->isPost()) { + $id = $this->request->post('id'); + if (is_array($id)) { + if (in_array($this->user->id, $id)) { + return $this->error('不能删除自己的账号!'); + } + } else { + if ($id == $this->user->id) { + return $this->error('不能删除自己的账号!'); + } + } + if (!UserModel::destroy($id)) { + return $this->error('删除失败'); + } + return $this->success('删除成功'); + } + } + + public function frozen() + { + if ($this->request->isPost()) { + $id = $this->request->post('id'); + if (is_array($id)) { + if (in_array($this->user->id, $id)) { + return $this->error('不能冻结自己的账号!'); + } + } else { + if ($id == $this->user->id) { + return $this->error('不能冻结自己的账号!'); + } + } + $model = new UserModel(); + if (!$model->where('id', 'in', $id)->update(['state' => 0])) { + return $this->error('冻结失败'); + } + return $this->success('冻结成功'); + } + } + + public function getUser() + { + if ($this->request->isPost()) { + $id = $this->request->post('id'); + if (!$user = UserModel::get($id)) { + return $this->error('数据获取失败'); + } + unset($user->password); + return $this->success('成功', null, $user); + } + } + + public function edit() + { + if ($this->request->isPost()) { + try { + $data = $this->request->post(); + $validate = $this->validate($data, 'Users.AdminEdit'); + if (true !== $validate) { + throw new Exception($validate); + } + if (!$data['password']) unset($data['password']); + UserModel::update($data); + } catch (Exception $e) { + return $this->error($e->getMessage()); + } + return $this->success('保存成功'); + } + } + + public function setState() + { + if ($this->request->isPost()) { + $id = $this->request->post('id'); + $state = $this->request->post('state'); + if (!$user = UserModel::get($id)) { + return $this->error('数据获取失败'); + } + if (!$user->where('id', $id)->setField('state', $state)) { + return $this->error('状态修改失败'); + } + return $this->success('状态修改成功'); + } + } +} \ No newline at end of file diff --git a/application/index/view/admin/images/index.html b/application/index/view/admin/images/index.html new file mode 100644 index 00000000..80bb1f85 --- /dev/null +++ b/application/index/view/admin/images/index.html @@ -0,0 +1,134 @@ +{extend name="common:base" /} + +{block name="title"}图片管理 - {$config.site_name}{/block} + +{block name="css"} + +{/block} + +{block name="main"} +
+
+
+ + 系统共有 {$images->total()} 张图片 + +
+
+
+ + + +
+
+
+ + + + + + + + + + + + + + {foreach $images as $key => $value} + + + + + + + + + + {/foreach} + +
所属用户储存策略路径大小类型上传时间操作
{$value.username}{$value.strategy}{$value.pathname}{$value.size|format_size}{$value.mime}{$value.create_time} + +
+ + +
+
+
+
+ {$images|raw} +
+ +
+
+{/block} + +{block name="js"} + + + +{/block} \ No newline at end of file diff --git a/application/index/view/admin/strategy/index.html b/application/index/view/admin/strategy/index.html new file mode 100644 index 00000000..70b488f5 --- /dev/null +++ b/application/index/view/admin/strategy/index.html @@ -0,0 +1,62 @@ +{extend name="common:base" /} + +{block name="title"}储存策略 - {$config.site_name}{/block} + +{block name="main"} +
+
+
+
+ + +
+
+ {foreach $strategy as $key => $value} + {$value.name} + {/foreach} +
+
+ {foreach $configs as $key => $value} +
+
+ {foreach $value as $val} +
+ + {switch $val.type} + {case text} + + {/case} + {/switch} +
+ {/foreach} + + + +
+
+ {/foreach} +
+
+
+
+{/block} +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/admin/system/index.html b/application/index/view/admin/system/index.html new file mode 100644 index 00000000..59ce403c --- /dev/null +++ b/application/index/view/admin/system/index.html @@ -0,0 +1,141 @@ +{extend name="common:base" /} + +{block name="title"}系统管理 - {$config.site_name}{/block} + +{block name="main"} +
+
+
+ +
+ {foreach $configs as $key => $value} + + {/foreach} +
+
+
+
+
+
+ + + + + + + + + + {foreach $naming.path as $value} + + + + + + {/foreach} + +
变量名示例说明
{$value.name}{$value.example}{$value.explain}
+
+
+
+
+
+
+ + + + + + + + + + {foreach $naming.file as $value} + + + + + + {/foreach} + +
变量名示例说明
{$value.name}{$value.example}{$value.explain}
+
+
+
+
+{/block} +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/admin/update/index.html b/application/index/view/admin/update/index.html new file mode 100644 index 00000000..bd03c588 --- /dev/null +++ b/application/index/view/admin/update/index.html @@ -0,0 +1,81 @@ +{extend name="common:base" /} + +{block name="title"}系统更新 - {$config.site_name}{/block} + +{block name="main"} +
+
+
+
+ + 当前版本:v{$config.system_version} +
+
+ {if $newest.version <= $config.system_version} + + {else /} +

检测到新版本!

+
+

v{$newest.version} [{$newest.name}] {$newest.date}

+
+ {foreach $newest.info as $k => $val} +

{$k + 1}. {$val}

+ {/foreach} +
+
+ + {/if} +
+

更新日志:

+ {if $record} + {foreach $record as $value} +
+

v{$value.version} [{$value.name}] {$value.date}

+
+ {foreach $value.info as $k => $val} +

{$k + 1}. {$val}

+ {/foreach} +
+
+ {/foreach} + {else /} +

暂无更新日志

+ {/if} +
+
+
+{/block} + +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/admin/users/index.html b/application/index/view/admin/users/index.html new file mode 100644 index 00000000..5644c3bd --- /dev/null +++ b/application/index/view/admin/users/index.html @@ -0,0 +1,216 @@ +{extend name="common:base" /} + +{block name="title"}用户管理 - {$config.site_name}{/block} + +{block name="main"} +
+
+
+ + 系统共有 {$users->total()} 个用户 + +
+
+
+ + + +
+
+
+ + + + + + + + + + + + + + + {foreach $users as $value} + + + + + + + + + + + {/foreach} + +
用户名昵称邮箱已使用容量总容量账号状态注册IP操作
{$value.username}{$value.nickname}{$value.email}{$value.use_quota|format_size}{$value.quota|format_size} + + {$value.reg_ip} +
+ + +
+
+
+
+ {$users|raw} +
+ +
+
+
+
+
修改用户数据
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+
+
+{/block} + +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/auth/forgot.html b/application/index/view/auth/forgot.html new file mode 100644 index 00000000..d3cd1222 --- /dev/null +++ b/application/index/view/auth/forgot.html @@ -0,0 +1,72 @@ +{extend name="common:base" /} + +{block name="title"}找回密码 - {$config.site_name}{/block} + +{block name="main"} +
+
+
+
+
+
找回密码
+
+
+
+ + +
+
+ + + 验证码 +
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+
+
+{/block} + +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/auth/login.html b/application/index/view/auth/login.html new file mode 100644 index 00000000..7e323c83 --- /dev/null +++ b/application/index/view/auth/login.html @@ -0,0 +1,46 @@ +{extend name="common:base" /} + +{block name="title"}登录 - {$config.site_name}{/block} + +{block name="css"} + +{/block} + +{block name="main"} +
+
+
+ +
+
+
+{/block} + +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/auth/register.html b/application/index/view/auth/register.html new file mode 100644 index 00000000..68466ec8 --- /dev/null +++ b/application/index/view/auth/register.html @@ -0,0 +1,62 @@ +{extend name="common:base" /} + +{block name="title"}注册 - {$config.site_name}{/block} + +{block name="css"} + +{/block} + +{block name="main"} +
+
+
+
+
+
注册账号
+
+
+ {include file="common:alert"} +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + 验证码 +
+
+ {if $config.close_register} + + {else /} + + {/if} +
+ {:token()} +
+

+ 已有账号?登录 +

+
+
+
+
+
+
+{/block} + +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/index/view/common/alert.html b/application/index/view/common/alert.html new file mode 100644 index 00000000..b0268726 --- /dev/null +++ b/application/index/view/common/alert.html @@ -0,0 +1,12 @@ +{if session('?success')} + +{/if} +{if session('?error')} + +{/if} \ No newline at end of file diff --git a/application/index/view/common/base.html b/application/index/view/common/base.html new file mode 100644 index 00000000..feb66244 --- /dev/null +++ b/application/index/view/common/base.html @@ -0,0 +1,159 @@ + + + + + + + + + + + {block name="title"}Lsky Pro{/block} + {block name="css"}{/block} + + + + + + +{block name="header"} +
+
+ + {$config.site_name} +
+ {if $user} + + + + {/if} + + + +
+
+{/block} +{block name="left"} + +{/block} +{block name="main"} +
+
+
+{/block} +{block name="footer"} +
+ +
+
+
+
+
+
+
+ +{/block} + + + +{block name="js"}{/block} + + + \ No newline at end of file diff --git a/application/index/view/index/index.html b/application/index/view/index/index.html new file mode 100644 index 00000000..4c71762c --- /dev/null +++ b/application/index/view/index/index.html @@ -0,0 +1,87 @@ +{extend name="common:base" /} + +{block name="title"}{$config.site_name}{/block} + +{block name="css"} + + +{/block} + +{block name="main"} +
+
+ {if (bool)$config.allowed_tourist_upload or $user} +
+
+

Image Upload

+

最大可上传 {:round($config.upload_max_size / 1024 / 1024)} MB的图片,单次同时可上传 {$config.upload_single_num} 张。

+
+
+ +
+
+ +
    +
      +
        +
          + +
          +
          + {else /} +
          +

          请登录后体验

          + 登录 + 注册 +
          + {/if} +
          +
          +{/block} +{block name="js"} + + + + +{/block} \ No newline at end of file diff --git a/application/index/view/install/index.html b/application/index/view/install/index.html new file mode 100644 index 00000000..8a96323d --- /dev/null +++ b/application/index/view/install/index.html @@ -0,0 +1,222 @@ + + + + Install Lsky Pro + + + + + + + + + +
          +
          + {switch stop} + + {case 1} +

          - 运行环境检测

          + + + + + + + + + + + + + + + + + + + +
          + PHP版本 ≥ 5.6: + + {if ($phpVerGt56) } + + {else/} + + {/if} +
          + Curl拓展: + + {if ($isCurl) } + + {else/} + + {/if} +
          + Zip拓展: + + {if ($isZip) } + + {else/} + + {/if} +
          + Mysqli: + + {if ($isMysqli) } + + {else/} + + {/if} +
          + {if (!$testing) } + 检测不通过 + {else/} + 下一步 + {/if} + {/case} + + {case 2} +

          - 完善数据库配置

          +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          + +
          + {/case} + + {case 3} +

          - 设置管理员账号

          +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          + +
          + {/case} + + {case 4} +

          - 安装成功

          +
          +

          程序安装成功,请牢记管理员账号密码!

          + 网站首页 + 作者博客 +
          +
          Copyright © 2018 Wisp X All rights reserved.
          + {/case} + + {default /} + + {/switch} +
          +
          + + + + + diff --git a/application/index/view/tpl/compatibility.html b/application/index/view/tpl/compatibility.html new file mode 100644 index 00000000..c677a54d --- /dev/null +++ b/application/index/view/tpl/compatibility.html @@ -0,0 +1,63 @@ + + + + + 抱歉,无法兼容您的IE版本 + + + +

          Oops!

          +

          本站不支持 IE 9 及以下版本浏览器。

          +
          +

          + 为了获得更好的浏览体验,我们强烈建议您 + 升级到最新版本的IE浏览器,或者使用较新版本的 + Google ChromeFirefox、 + Safari 等。 如果您使用的是 IE 10 或以上版本,请关闭“兼容性视图”。 +

          + + diff --git a/application/index/view/user/images.html b/application/index/view/user/images.html new file mode 100644 index 00000000..50442e18 --- /dev/null +++ b/application/index/view/user/images.html @@ -0,0 +1,346 @@ +{extend name="common:base" /} + +{block name="title"}图片管理 - {$config.site_name}{/block} + +{block name="css"} + + +{/block} + +{block name="main"} +
          +
          +
          +
          +
          + + 共有 0 张图片 + +
          +
          +
          + +
          + +
          + +
          +
          +
          +
          +
          +
          +
          + + +
          +
          +
          +
          +
          + +
          +
          + + + + + + + + + + + + + + + + + + + + + + + +
          链接: + +
          位置:
          大小:
          MD5:
          类型:
          +
          +
          +
          +
          +
          + 打开链接 + + + +
          +
          +
          +
          +
          +
          + + +{/block} + +{block name="js"} + + + + + +{/block} \ No newline at end of file diff --git a/application/index/view/user/settings.html b/application/index/view/user/settings.html new file mode 100644 index 00000000..44d501ca --- /dev/null +++ b/application/index/view/user/settings.html @@ -0,0 +1,54 @@ +{extend name="common:base" /} + +{block name="title"}设置 - {$config.site_name}{/block} + +{block name="main"} +
          +
          +
          +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          + + +
          +
          +
          +
          +
          +{/block} +{block name="js"} + +{/block} \ No newline at end of file diff --git a/application/provider.php b/application/provider.php new file mode 100644 index 00000000..a8415bde --- /dev/null +++ b/application/provider.php @@ -0,0 +1,13 @@ + +// +---------------------------------------------------------------------- + +// 应用容器绑定定义 +return []; diff --git a/application/tags.php b/application/tags.php new file mode 100644 index 00000000..4b18d105 --- /dev/null +++ b/application/tags.php @@ -0,0 +1,28 @@ + +// +---------------------------------------------------------------------- + +// 应用行为扩展定义文件 +return [ + // 应用初始化 + 'app_init' => [], + // 应用开始 + 'app_begin' => [], + // 模块初始化 + 'module_init' => [], + // 操作开始执行 + 'action_begin' => [], + // 视图内容过滤 + 'view_filter' => [], + // 日志写入 + 'log_write' => [], + // 应用结束 + 'app_end' => [], +]; diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..43320bb8 --- /dev/null +++ b/composer.json @@ -0,0 +1,33 @@ +{ + "name": "wisp-x/lsky-pro", + "description": "Lsky Pro, your photo album on the cloud.", + "type": "project", + "keywords": [ + "lsky", + "lsky Pro" + ], + "homepage": "http://lsys.wispx.cn/", + "license": "GPL-3.0-only", + "authors": [ + { + "name": "Wisp X", + "email": "1591788658@qq.com" + } + ], + "require": { + "php": ">=5.6.0", + "topthink/framework": "5.1.*", + "topthink/think-captcha": "^2.0", + "aliyuncs/oss-sdk-php": "^2.3", + "qiniu/php-sdk": "^7.2", + "upyun/sdk": "^3.3", + "qcloud/cos-sdk-v5": "^1.2", + "topthink/think-image": "^1.0", + "phpmailer/phpmailer": "^6.0" + }, + "autoload": { + "psr-4": { + "app\\": "application" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..3a2d15c7 --- /dev/null +++ b/composer.lock @@ -0,0 +1,842 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "f3a07db17ca2f758f19303939f3bb3d6", + "packages": [ + { + "name": "aliyuncs/oss-sdk-php", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/aliyun/aliyun-oss-php-sdk.git", + "reference": "e69f57916678458642ac9d2fd341ae78a56996c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/e69f57916678458642ac9d2fd341ae78a56996c8", + "reference": "e69f57916678458642ac9d2fd341ae78a56996c8", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "~1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "OSS\\": "src/OSS" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aliyuncs", + "homepage": "http://www.aliyun.com" + } + ], + "description": "Aliyun OSS SDK for PHP", + "homepage": "http://www.aliyun.com/product/oss/", + "time": "2018-01-08T06:59:35+00:00" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.9-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "abandoned": "guzzlehttp/guzzle", + "time": "2015-03-18T18:23:50+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2018-04-22T15:46:56+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-03-20T17:10:46+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.0.5", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "cb3ea134d4d3729e7857737d5f320cce9caf4d32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/cb3ea134d4d3729e7857737d5f320cce9caf4d32", + "reference": "cb3ea134d4d3729e7857737d5f320cce9caf4d32", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "doctrine/annotations": "1.2.*", + "friendsofphp/php-cs-fixer": "^2.2", + "phpdocumentor/phpdocumentor": "2.*", + "phpunit/phpunit": "^4.8 || ^5.7", + "zendframework/zend-eventmanager": "3.0.*", + "zendframework/zend-i18n": "2.7.3", + "zendframework/zend-serializer": "2.7.*" + }, + "suggest": { + "ext-mbstring": "Needed to send email in multibyte encoding charset", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "time": "2018-03-27T13:49:45+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "qcloud/cos-sdk-v5", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/tencentyun/cos-php-sdk-v5.git", + "reference": "50d99c6ebfa32d3715fc5736340ac904b7398c93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tencentyun/cos-php-sdk-v5/zipball/50d99c6ebfa32d3715fc5736340ac904b7398c93", + "reference": "50d99c6ebfa32d3715fc5736340ac904b7398c93", + "shasum": "" + }, + "require": { + "guzzle/guzzle": "~3.7", + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Qcloud\\Cos\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "yaozongyou", + "email": "yaozongyou@vip.qq.com" + }, + { + "name": "lewzylu", + "email": "327874225@qq.com" + } + ], + "description": "PHP SDK for QCloud COS", + "keywords": [ + "cos", + "php", + "qcloud" + ], + "time": "2018-05-28T08:35:28+00:00" + }, + { + "name": "qiniu/php-sdk", + "version": "v7.2.6", + "source": { + "type": "git", + "url": "https://github.com/qiniu/php-sdk.git", + "reference": "305ce1c1c0c71f794661fe45a96facf61ef96c5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/qiniu/php-sdk/zipball/305ce1c1c0c71f794661fe45a96facf61ef96c5d", + "reference": "305ce1c1c0c71f794661fe45a96facf61ef96c5d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Qiniu\\": "src/Qiniu" + }, + "files": [ + "src/Qiniu/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Qiniu", + "email": "sdk@qiniu.com", + "homepage": "http://www.qiniu.com" + } + ], + "description": "Qiniu Resource (Cloud) Storage SDK for PHP", + "homepage": "http://developer.qiniu.com/", + "keywords": [ + "cloud", + "qiniu", + "sdk", + "storage" + ], + "time": "2018-05-18T04:37:29+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.8.42", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "9b69aad7d4c086dc94ebade2d5eb9145da5dac8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9b69aad7d4c086dc94ebade2d5eb9145da5dac8c", + "reference": "9b69aad7d4c086dc94ebade2d5eb9145da5dac8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2018-04-06T07:35:03+00:00" + }, + { + "name": "topthink/framework", + "version": "v5.1.19", + "source": { + "type": "git", + "url": "https://github.com/top-think/framework.git", + "reference": "3a0fea90ed2a99b181ce503090e08be1171ed091" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/framework/zipball/3a0fea90ed2a99b181ce503090e08be1171ed091", + "reference": "3a0fea90ed2a99b181ce503090e08be1171ed091", + "shasum": "" + }, + "require": { + "php": ">=5.6.0", + "topthink/think-installer": "2.*" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.0", + "mikey179/vfsstream": "~1.6", + "phpdocumentor/reflection-docblock": "^2.0", + "phploc/phploc": "2.*", + "phpunit/phpunit": "^5.0|^6.0", + "sebastian/phpcpd": "2.*", + "squizlabs/php_codesniffer": "2.*" + }, + "type": "think-framework", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "liu21st", + "email": "liu21st@gmail.com" + }, + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "description": "the new thinkphp framework", + "homepage": "http://thinkphp.cn/", + "keywords": [ + "framework", + "orm", + "thinkphp" + ], + "time": "2018-07-13T14:10:28+00:00" + }, + { + "name": "topthink/think-captcha", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-captcha.git", + "reference": "54c8a51552f99ff9ea89ea9c272383a8f738ceee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-captcha/zipball/54c8a51552f99ff9ea89ea9c272383a8f738ceee", + "reference": "54c8a51552f99ff9ea89ea9c272383a8f738ceee", + "shasum": "" + }, + "require": { + "topthink/framework": "5.1.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "think\\captcha\\": "src/" + }, + "files": [ + "src/helper.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "description": "captcha package for thinkphp5", + "time": "2017-12-31T16:37:49+00:00" + }, + { + "name": "topthink/think-image", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-image.git", + "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-image/zipball/8586cf47f117481c6d415b20f7dedf62e79d5512", + "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512", + "shasum": "" + }, + "require": { + "ext-gd": "*" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*", + "topthink/framework": "^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "think\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "description": "The ThinkPHP5 Image Package", + "time": "2016-09-29T06:05:43+00:00" + }, + { + "name": "topthink/think-installer", + "version": "v2.0.0", + "source": { + "type": "git", + "url": "https://github.com/top-think/think-installer.git", + "reference": "f5400a12c60e513911aef41fe443fa6920952675" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/top-think/think-installer/zipball/f5400a12c60e513911aef41fe443fa6920952675", + "reference": "f5400a12c60e513911aef41fe443fa6920952675", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev" + }, + "type": "composer-plugin", + "extra": { + "class": "think\\composer\\Plugin" + }, + "autoload": { + "psr-4": { + "think\\composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "yunwuxin", + "email": "448901948@qq.com" + } + ], + "time": "2018-05-11T06:45:42+00:00" + }, + { + "name": "upyun/sdk", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/upyun/php-sdk.git", + "reference": "1a2dd5ae31047956c733aef0f764f3a527d30628" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/upyun/php-sdk/zipball/1a2dd5ae31047956c733aef0f764f3a527d30628", + "reference": "1a2dd5ae31047956c733aef0f764f3a527d30628", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "guzzlehttp/guzzle": "~6.0", + "php": ">=5.5.0" + }, + "require-dev": { + "consolidation/robo": "^1.0", + "phpdocumentor/phpdocumentor": "^2.9", + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Upyun\\": "src/Upyun/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "totoleo", + "email": "totoleo@163.com" + }, + { + "name": "lfeng", + "email": "bonevv@gmail.com" + }, + { + "name": "lvtongda", + "email": "riyao.lyu@gmail.com" + }, + { + "name": "sabakugaara", + "email": "senellise@gmail.com" + } + ], + "description": "UPYUN sdk for php", + "homepage": "https://github.com/upyun/php-sdk/", + "keywords": [ + "sdk", + "upyun" + ], + "time": "2017-11-12T09:17:42+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.6.0" + }, + "platform-dev": [] +} diff --git a/config/app.php b/config/app.php new file mode 100644 index 00000000..78cc9784 --- /dev/null +++ b/config/app.php @@ -0,0 +1,146 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 应用设置 +// +---------------------------------------------------------------------- + +return [ + // 应用名称 + 'app_name' => 'Lsky Pro', + // 应用地址 + 'app_host' => '', + // 应用调试模式 + 'app_debug' => false, + // 应用Trace + 'app_trace' => false, + // 是否支持多模块 + 'app_multi_module' => true, + // 入口自动绑定模块 + 'auto_bind_module' => false, + // 注册的根命名空间 + 'root_namespace' => ['app'], + // 默认输出类型 + 'default_return_type' => 'html', + // 默认AJAX 数据返回格式,可选json xml ... + 'default_ajax_return' => 'json', + // 默认JSONP格式返回的处理方法 + 'default_jsonp_handler' => 'jsonpReturn', + // 默认JSONP处理方法 + 'var_jsonp_handler' => 'callback', + // 默认时区 + 'default_timezone' => 'Asia/Shanghai', + // 是否开启多语言 + 'lang_switch_on' => false, + // 默认全局过滤方法 用逗号分隔多个 + 'default_filter' => '', + // 默认语言 + 'default_lang' => 'zh-cn', + // 应用类库后缀 + 'class_suffix' => false, + // 控制器类后缀 + 'controller_suffix' => false, + + // +---------------------------------------------------------------------- + // | 模块设置 + // +---------------------------------------------------------------------- + + // 默认模块名 + 'default_module' => 'index', + // 禁止访问模块 + 'deny_module_list' => ['common'], + // 默认控制器名 + 'default_controller' => 'Index', + // 默认操作名 + 'default_action' => 'index', + // 默认验证器 + 'default_validate' => '', + // 默认的空模块名 + 'empty_module' => '', + // 默认的空控制器名 + 'empty_controller' => 'Error', + // 操作方法前缀 + 'use_action_prefix' => false, + // 操作方法后缀 + 'action_suffix' => '', + // 自动搜索控制器 + 'controller_auto_search' => true, + + // +---------------------------------------------------------------------- + // | URL设置 + // +---------------------------------------------------------------------- + + // PATHINFO变量名 用于兼容模式 + 'var_pathinfo' => 's', + // 兼容PATH_INFO获取 + 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], + // pathinfo分隔符 + 'pathinfo_depr' => '/', + // HTTPS代理标识 + 'https_agent_name' => '', + // IP代理获取标识 + 'http_agent_ip' => 'X-REAL-IP', + // URL伪静态后缀 + 'url_html_suffix' => 'html', + // URL普通方式参数 用于自动生成 + 'url_common_param' => true, + // URL参数方式 0 按名称成对解析 1 按顺序解析 + 'url_param_type' => 0, + // 是否开启路由延迟解析 + 'url_lazy_route' => false, + // 是否强制使用路由 + 'url_route_must' => false, + // 合并路由规则 + 'route_rule_merge' => false, + // 路由是否完全匹配 + 'route_complete_match' => true, + // 使用注解路由 + 'route_annotation' => false, + // 域名根,如thinkphp.cn + 'url_domain_root' => '', + // 是否自动转换URL中的控制器和操作名 + 'url_convert' => false, + // 默认的访问控制器层 + 'url_controller_layer' => 'controller', + // 表单请求类型伪装变量 + 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', + // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 + 'request_cache' => false, + // 请求缓存有效期 + 'request_cache_expire' => null, + // 全局请求缓存排除规则 + 'request_cache_except' => [], + // 是否开启路由缓存 + 'route_check_cache' => false, + // 路由缓存的Key自定义设置(闭包),默认为当前URL和请求类型的md5 + 'route_check_cache_key' => '', + // 路由缓存类型及参数 + 'route_cache_option' => [], + + // 默认跳转页面对应的模板文件 + 'dispatch_success_tmpl' => Env::get('think_path') . 'tpl/dispatch_jump.tpl', + 'dispatch_error_tmpl' => Env::get('think_path') . 'tpl/dispatch_jump.tpl', + + // 异常页面的模板文件 + 'exception_tmpl' => Env::get('think_path') . 'tpl/think_exception.tpl', + + // 错误显示信息,非调试模式有效 + 'error_message' => '页面错误!请稍后再试~', + // 显示错误信息 + 'show_error_msg' => false, + // 异常处理handle类 留空使用 \think\exception\Handle + 'exception_handle' => '', + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 00000000..985dbb1c --- /dev/null +++ b/config/cache.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 缓存设置 +// +---------------------------------------------------------------------- + +return [ + // 驱动方式 + 'type' => 'File', + // 缓存保存目录 + 'path' => '', + // 缓存前缀 + 'prefix' => '', + // 缓存有效期 0表示永久缓存 + 'expire' => 0, +]; diff --git a/config/console.php b/config/console.php new file mode 100644 index 00000000..b0f9165a --- /dev/null +++ b/config/console.php @@ -0,0 +1,19 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 控制台配置 +// +---------------------------------------------------------------------- +return [ + 'name' => 'Think Console', + 'version' => '0.1', + 'user' => null, +]; diff --git a/config/cookie.php b/config/cookie.php new file mode 100644 index 00000000..1de07082 --- /dev/null +++ b/config/cookie.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | Cookie设置 +// +---------------------------------------------------------------------- +return [ + // cookie 名称前缀 + 'prefix' => '', + // cookie 保存时间 + 'expire' => 0, + // cookie 保存路径 + 'path' => '/', + // cookie 有效域名 + 'domain' => '', + // cookie 启用安全传输 + 'secure' => false, + // httponly设置 + 'httponly' => '', + // 是否使用 setcookie + 'setcookie' => true, +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 00000000..6afcc175 --- /dev/null +++ b/config/database.php @@ -0,0 +1,63 @@ + +// +---------------------------------------------------------------------- + +return [ + // 数据库类型 + 'type' => 'mysql', + // 服务器地址 + 'hostname' => '{hostname}', + // 数据库名 + 'database' => '{database}', + // 用户名 + 'username' => '{username}', + // 密码 + 'password' => '{password}', + // 端口 + 'hostport' => '{hostport}', + // 连接dsn + 'dsn' => '', + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => 'utf8', + // 数据库表前缀 + 'prefix' => 'lsky_', + // 数据库调试模式 + 'debug' => true, + // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) + 'deploy' => 0, + // 数据库读写是否分离 主从式有效 + 'rw_separate' => false, + // 读写分离后 主服务器数量 + 'master_num' => 1, + // 指定从服务器序号 + 'slave_no' => '', + // 自动读取主库数据 + 'read_master' => false, + // 是否严格检查字段是否存在 + 'fields_strict' => true, + // 数据集返回类型 + 'resultset_type' => 'array', + // 自动写入时间戳字段 + 'auto_timestamp' => true, + // 时间字段取出后的默认时间格式 + 'datetime_format' => 'Y-m-d H:i:s', + // 是否需要进行SQL性能分析 + 'sql_explain' => false, + // Builder类 + 'builder' => '', + // Query类 + 'query' => '\\think\\db\\Query', + // 是否需要断线重连 + 'break_reconnect' => false, + // 断线标识字符串 + 'break_match_str' => [], +]; diff --git a/config/log.php b/config/log.php new file mode 100644 index 00000000..b3d87b4a --- /dev/null +++ b/config/log.php @@ -0,0 +1,30 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 日志设置 +// +---------------------------------------------------------------------- +return [ + // 日志记录方式,内置 file socket 支持扩展 + 'type' => 'File', + // 日志保存目录 + 'path' => '', + // 日志记录级别 + 'level' => [], + // 单文件日志写入 + 'single' => false, + // 独立日志级别 + 'apart_level' => [], + // 最大日志文件数量 + 'max_files' => 0, + // 是否关闭日志写入 + 'close' => false, +]; diff --git a/config/middleware.php b/config/middleware.php new file mode 100644 index 00000000..e9978f79 --- /dev/null +++ b/config/middleware.php @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 中间件配置 +// +---------------------------------------------------------------------- +return [ + 'auth' => app\http\middleware\Auth::class +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 00000000..1d7b6c60 --- /dev/null +++ b/config/session.php @@ -0,0 +1,26 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 会话设置 +// +---------------------------------------------------------------------- + +return [ + 'id' => '', + // SESSION_ID的提交变量,解决flash上传跨域 + 'var_session_id' => '', + // SESSION 前缀 + 'prefix' => 'think', + // 驱动方式 支持redis memcache memcached + 'type' => '', + // 是否自动开启 SESSION + 'auto_start' => true, +]; diff --git a/config/template.php b/config/template.php new file mode 100644 index 00000000..299bd6f4 --- /dev/null +++ b/config/template.php @@ -0,0 +1,35 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | 模板设置 +// +---------------------------------------------------------------------- + +return [ + // 模板引擎类型 支持 php think 支持扩展 + 'type' => 'Think', + // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法 + 'auto_rule' => 1, + // 模板路径 + 'view_path' => '', + // 模板后缀 + 'view_suffix' => 'html', + // 模板文件名分隔符 + 'view_depr' => DIRECTORY_SEPARATOR, + // 模板引擎普通标签开始标记 + 'tpl_begin' => '{', + // 模板引擎普通标签结束标记 + 'tpl_end' => '}', + // 标签库标签开始标记 + 'taglib_begin' => '{', + // 标签库标签结束标记 + 'taglib_end' => '}', +]; diff --git a/config/trace.php b/config/trace.php new file mode 100644 index 00000000..425d3014 --- /dev/null +++ b/config/trace.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- + +// +---------------------------------------------------------------------- +// | Trace设置 开启 app_trace 后 有效 +// +---------------------------------------------------------------------- +return [ + // 内置Html Console 支持扩展 + 'type' => 'Html', +]; diff --git a/extend/strategy/Driver.php b/extend/strategy/Driver.php new file mode 100644 index 00000000..8f2c3156 --- /dev/null +++ b/extend/strategy/Driver.php @@ -0,0 +1,50 @@ +options = $options; + try { + $this->cos = new Client([ + 'region' => $this->options['cos_region'], + 'credentials' => [ + 'secretId' => $this->options['cos_secret_id'], + 'secretKey' => $this->options['cos_secret_key'], + ], + ]); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + } + } + + /** + * 创建文件 + * + * @param $pathname + * @param $file + * + * @return bool + */ + public function create($pathname, $file) + { + try { + $this->cos->putObject(array( + 'Bucket' => $this->options['cos_bucket'], + 'Key' => $pathname, + 'Body' => fopen($file, 'rb') + )); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + /** + * 删除文件 + * + * @param $pathname + * + * @return bool + */ + public function delete($pathname) + { + try { + $this->cos->deleteObject([ + 'Bucket' => $this->options['cos_bucket'], + 'Key' => $pathname, + ]); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + /** + * 删除多个文件 + * + * @param array $list + * @return bool|mixed + */ + public function deletes(array $list) + { + try { + foreach ($list as $value) { + if (is_string($value)) { + $this->cos->deleteObject([ + 'Bucket' => $this->options['cos_bucket'], + 'Key' => $value, + ]); + } + } + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + public function getError() + { + return 'Cos:' . $this->error; + } +} \ No newline at end of file diff --git a/extend/strategy/driver/Local.php b/extend/strategy/driver/Local.php new file mode 100644 index 00000000..2d3a69a0 --- /dev/null +++ b/extend/strategy/driver/Local.php @@ -0,0 +1,130 @@ +options = $options; + } + + /** + * 创建本地文件 + * + * @param $pathname 文件路径加文件名 + * @param $file 文件资源路径 + * + * @return bool + */ + public function create($pathname, $file) + { + $path = self::ROOT_PATH . dirname($pathname) . DIRECTORY_SEPARATOR; + if (true === $this->checkPath($path)) { + if (move_uploaded_file($file, $pathname)) { + return true; + } + } + $this->error = '文件移动失败'; + + return false; + } + + /** + * 删除本地文件 + * + * @param $pathname 文件路径加文件名 + * + * @return bool + */ + public function delete($pathname) + { + $delete = unlink(self::ROOT_PATH . ltrim($pathname, DIRECTORY_SEPARATOR)); + if (!$delete) { + $this->error = '文件删除失败'; + } + + return $delete; + } + + /** + * 批量删除本地文件 + * + * @param array $list + * @return bool|mixed + */ + public function deletes(array $list) + { + foreach ($list as $value) { + if (is_string($value)) { + @unlink(self::ROOT_PATH . ltrim($value, DIRECTORY_SEPARATOR)); + } + } + + return true; + } + + /** + * 检测目录是否可写,不存在则创建 + * + * @param $path 路径 + * + * @return bool + */ + protected function checkPath($path) + { + if (is_dir($path)) { + return true; + } + + if (mkdir($path, 0755, true)) { + return true; + } + $this->error = '目录[' . $path . ']无写入权限'; + + return false; + } + + public function getError() + { + return 'Local:' . $this->error; + } +} \ No newline at end of file diff --git a/extend/strategy/driver/Oss.php b/extend/strategy/driver/Oss.php new file mode 100644 index 00000000..a57fe86c --- /dev/null +++ b/extend/strategy/driver/Oss.php @@ -0,0 +1,127 @@ +options = $options; + try { + $this->oss = new OssClient( + $this->options['oss_access_key_id'], + $this->options['oss_access_key_secret'], + $this->options['oss_endpoint'], + false, + null, + null + ); + } catch (OssException $e) { + $this->error = $e->getMessage(); + } + } + + /** + * 创建文件 + * + * @param $pathname + * @param $file + * + * @return bool + */ + public function create($pathname, $file) + { + try { + $this->oss->uploadFile($this->options['oss_bucket'], $pathname, $file); + } catch (OssException $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + /** + * 删除文件 + * + * @param $pathname + * + * @return bool + */ + public function delete($pathname) + { + try { + $this->oss->deleteObject($this->options['oss_bucket'], $pathname); + } catch (OssException $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + /** + * 删除多个文件 + * + * @param array $list + * @return bool|mixed + */ + public function deletes(array $list) + { + try { + $this->oss->deleteObjects($this->options['oss_bucket'], $list); + } catch (OssException $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + public function getError() + { + return 'Oss:' . $this->error; + } +} \ No newline at end of file diff --git a/extend/strategy/driver/Qiniu.php b/extend/strategy/driver/Qiniu.php new file mode 100644 index 00000000..31fcba4e --- /dev/null +++ b/extend/strategy/driver/Qiniu.php @@ -0,0 +1,141 @@ +options = $options; + try { + $auth = new Auth($this->options['qiniu_access_key'], $this->options['qiniu_secret_key']); + $this->uploadToken = $auth->uploadToken($this->options['qiniu_bucket']); + $config = new Config(); + $this->uploadMgr = new UploadManager(); + $this->bucketMgr = new BucketManager($auth, $config); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + } + } + + /** + * 创建一个文件 + * + * @param $pathname + * @param $file + * + * @return bool + * @throws \Exception + */ + public function create($pathname, $file) + { + list($ret, $err) = $this->uploadMgr->putFile($this->uploadToken, $pathname, $file); + if (null !== $err) { + $this->error = $err; + return false; + } + + return true; + } + + /** + * 删除文件 + * + * @param $pathname + * + * @return bool + */ + public function delete($pathname) + { + $err = $this->bucketMgr->delete($this->options['qiniu_bucket'], $pathname); + if ($err) { + $this->error = $err; + return false; + } + + return true; + } + + /** + * 删除多个文件 + * + * @param array $list + * @return bool|mixed + */ + public function deletes(array $list) + { + foreach ($list as $value) { + if (is_string($value)) { + $err = $this->bucketMgr->delete($this->options['qiniu_bucket'], $value); + if ($err) { + $this->error = $err; + } + } + } + + return true; + } + + public function getError() + { + return 'Qiniu:' . $this->error; + } +} \ No newline at end of file diff --git a/extend/strategy/driver/Upyun.php b/extend/strategy/driver/Upyun.php new file mode 100644 index 00000000..640d59a8 --- /dev/null +++ b/extend/strategy/driver/Upyun.php @@ -0,0 +1,128 @@ +options = $options; + try { + $serviceConfig = new \Upyun\Config( + $this->options['upyun_service_name'], + $this->options['upyun_operator_name'], + $this->options['upyun_operator_pwd'] + ); + $this->upyun = new \Upyun\Upyun($serviceConfig); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + } + } + + /** + * 创建文件 + * + * @param $pathname + * @param $file + * + * @return bool + */ + public function create($pathname, $file) + { + try { + $this->upyun->write($pathname, fopen($file, 'r')); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + /** + * 删除文件 + * + * @param $pathname + * + * @return bool + */ + public function delete($pathname) + { + try { + $this->upyun->delete($pathname); + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + /** + * 删除多个文件 + * + * @param array $list + * @return bool|mixed + */ + public function deletes(array $list) + { + try { + foreach ($list as $value) { + if (is_string($value)) { + // 异步删除 + $this->upyun->delete($value, true); + } + } + } catch (\Exception $e) { + $this->error = $e->getMessage(); + return false; + } + + return true; + } + + public function getError() + { + return 'Upyun:' . $this->error; + } +} \ No newline at end of file diff --git a/install.sql b/install.sql new file mode 100644 index 00000000..34ba3807 --- /dev/null +++ b/install.sql @@ -0,0 +1,171 @@ +-- phpMyAdmin SQL Dump +-- version 4.8.2 +-- https://www.phpmyadmin.net/ +-- +-- Host: localhost:3306 +-- Generation Time: 2018-09-28 17:54:47 +-- 服务器版本: 5.7.21 +-- PHP Version: 7.2.7 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + +-- +-- Database: `lsky` +-- + +-- -------------------------------------------------------- + +-- +-- 表的结构 `lsky_config` +-- + +CREATE TABLE `lsky_config` ( + `id` smallint(6) UNSIGNED NOT NULL, + `key` varchar(32) DEFAULT NULL COMMENT 'key', + `type` varchar(32) NOT NULL DEFAULT 'text' COMMENT 'text|bool|textarea|select', + `input_type` varchar(32) NOT NULL DEFAULT 'text' COMMENT 'input type属性', + `name` varchar(32) CHARACTER SET utf8mb4 NOT NULL COMMENT '配置名', + `title` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '配置标题', + `tip` varchar(100) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '配置描述', + `value` text CHARACTER SET utf8mb4 NOT NULL COMMENT '配置值', + `extend` text CHARACTER SET utf8mb4 NOT NULL COMMENT '扩展属性' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置' ROW_FORMAT=COMPACT; + +-- +-- 转存表中的数据 `lsky_config` +-- + +INSERT INTO `lsky_config` (`id`, `key`, `type`, `input_type`, `name`, `title`, `tip`, `value`, `extend`) VALUES +(1, 'basics', 'bool', 'checkbox', 'close_register', '关闭注册', NULL, '0', ''), +(2, 'basics', 'text', 'text', 'site_name', '网站标题', NULL, 'Lsky Pro', ''), +(3, 'basics', 'text', 'text', 'site_keywords', '网站关键字', NULL, 'Lsky Pro', ''), +(4, 'basics', 'text', 'text', 'site_description', '网站描述', NULL, 'Lsky Pro', ''), +(5, 'basics', 'text', 'text', 'icp_number', '备案号', NULL, '皖ICP备16011445号', ''), +(6, 'upload', 'bool', 'checkbox', 'allowed_tourist_upload', '允许游客上传', '是否允许游客上传', '1', ''), +(7, 'upload', 'text', 'text', 'upload_max_size', '最大上传大小', '单位:b,默认5242880:5M', '5242880', ''), +(8, 'upload', 'text', 'number', 'upload_single_num', '单次同时上传数量', NULL, '10', ''), +(9, 'upload', 'text', 'text', 'upload_allowed_exts', '允许上传的文件后缀', '逗号隔开', 'jpg,jpeg,gif,png,ico', ''), +(10, 'upload', 'text', 'text', 'path_naming_rule', '文件路径命名规则', '路径命名变量对照表', '{Y}/{m}/{d}', ''), +(11, 'upload', 'text', 'text', 'file_naming_rule', '文件路径命名规则', '文件命名变量对照表', '{uniqid}', ''), +(12, 'user', 'text', 'text', 'user_initial_quota', '用户初始配额容量', '单位:b,默认1073741824:1G,最大18位', '1073741824', ''), +(13, 'mail', 'select', 'text', 'mail_send_mode', '邮件发送方式', NULL, 'smtp', '{\"smtp\":\"SMTP\",\"mail\":\"Mail\"}'), +(14, 'mail', 'select', 'text', 'mail_smtp_secure', 'SMTP验证方式', NULL, 'none', '{\"none\":\"None\",\"tls\":\"TLS\",\"ssl\":\"SSL\"}'), +(15, 'mail', 'text', 'text', 'mail_smtp_host', 'SMTP主机地址', NULL, '', ''), +(16, 'mail', 'text', 'text', 'mail_smtp_username', 'SMTP用户名', NULL, '', ''), +(17, 'mail', 'text', 'password', 'mail_smtp_password', 'SMTP密码', NULL, '', ''), +(18, 'mail', 'text', 'number', 'mail_smtp_port', 'SMTP端口', '25/465', '25', ''), +(19, 'mail', 'text', 'email', 'mail_form_email', '发件人邮箱', NULL, '', ''), +(20, 'other', 'bool', 'checkbox', 'soft_delete', '软删除', '删除图片时不删除源文件,不建议开启', '0', ''), + +(21, 'storage_strategy', 'select', 'text', 'storage_strategy', '储存策略', NULL, 'local', ''), +(22, 'local', 'text', 'text', 'local_cdn_domain', 'CDN加速域名', '为空使用当前站点域名', '', ''), +(23, 'oss', 'text', 'text', 'oss_cdn_domain', 'Bucket域名', '为空使用当前站点域名', '', ''), +(24, 'oss', 'text', 'text', 'oss_access_key_id', 'AccessKeyId', NULL, '', ''), +(25, 'oss', 'text', 'text', 'oss_access_key_secret', 'AccessKeySecret', NULL, '', ''), +(26, 'oss', 'text', 'text', 'oss_endpoint', 'Endpoint', '地域节点', '', ''), +(27, 'oss', 'text', 'text', 'oss_bucket', 'Bucket', NULL, '', ''), +(28, 'cos', 'text', 'text', 'cos_cdn_domain', 'CDN加速域名', '为空使用当前站点域名,储存桶->域名管理->加速域名', '', ''), +(29, 'cos', 'text', 'text', 'cos_secret_id', 'SecretId', NULL, '', ''), +(30, 'cos', 'text', 'text', 'cos_secret_key', 'SecretKey', NULL, '', ''), +(31, 'cos', 'text', 'text', 'cos_region', '所属地域', NULL, '', ''), +(32, 'cos', 'text', 'text', 'cos_bucket', 'Bucket', '储存桶名称', '', ''), +(33, 'qiniu', 'text', 'text', 'qiniu_cdn_domain', 'CDN加速域名', '为空使用当前站点域名', '', ''), +(34, 'qiniu', 'text', 'text', 'qiniu_access_key', 'AccessKey', NULL, '', ''), +(35, 'qiniu', 'text', 'text', 'qiniu_secret_key', 'SecretKey', NULL, '', ''), +(36, 'qiniu', 'text', 'text', 'qiniu_bucket', 'Bucket', NULL, '', ''), +(37, 'upyun', 'text', 'text', 'upyun_cdn_domain', 'CDN加速域名', '为空使用当前站点域名', '', ''), +(38, 'upyun', 'text', 'text', 'upyun_operator_name', 'OperatorName', '操作员账号', '', ''), +(39, 'upyun', 'text', 'password', 'upyun_operator_pwd', 'OperatorPwd', '操作员密码', '', ''), +(40, 'upyun', 'text', 'text', 'upyun_service_name', 'ServiceName', '云储存服务名称', '', ''), +(41, '', 'text', 'text', 'system_version', '系统版本', NULL, '1.0', ''); + + +-- -------------------------------------------------------- + +-- +-- 表的结构 `lsky_images` +-- + +CREATE TABLE `lsky_images` ( + `id` int(11) UNSIGNED NOT NULL COMMENT 'ID', + `user_id` int(11) NOT NULL DEFAULT 0 COMMENT '用户ID,为0表示游客上传', + `strategy` varchar(32) NOT NULL DEFAULT 'local' COMMENT '储存策略,默认本地', + `path` varchar(500) NOT NULL COMMENT '保存路径', + `name` varchar(500) NOT NULL COMMENT '保存名称', + `pathname` varchar(500) NOT NULL COMMENT '保存名称', + `size` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '图片大小(字节:b)', + `mime` varchar(32) NOT NULL COMMENT '文件MIME类型', + `sha1` varchar(100) NOT NULL COMMENT 'hash sha1', + `md5` varchar(32) NOT NULL COMMENT 'hash md5', + `create_time` int(11) NOT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图片表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `lsky_users` +-- + +CREATE TABLE `lsky_users` ( + `id` int(11) UNSIGNED NOT NULL, + `username` varchar(32) NOT NULL COMMENT '用户名', + `nickname` varchar(32) DEFAULT NULL COMMENT '昵称', + `email` varchar(100) NOT NULL COMMENT '邮箱', + `password` varchar(32) NOT NULL COMMENT '密码', + `quota` decimal(20,2) NOT NULL DEFAULT '0.00' COMMENT '可用配额容量(字节:b)', + `is_admin` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否为管理员', + `state` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0:冻结,1:正常', + `token` varchar(32) DEFAULT NULL COMMENT 'Token', + `reg_ip` varchar(32) DEFAULT NULL COMMENT '注册IP', + `delete_time` int(11) DEFAULT NULL COMMENT '删除时间', + `update_time` int(11) NOT NULL COMMENT '更新时间', + `create_time` int(11) NOT NULL COMMENT '添加时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `lsky_config` +-- +ALTER TABLE `lsky_config` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`); + +-- +-- Indexes for table `lsky_images` +-- +ALTER TABLE `lsky_images` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `lsky_users` +-- +ALTER TABLE `lsky_users` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `username` (`username`), + ADD UNIQUE KEY `email` (`email`); + +-- +-- 在导出的表使用AUTO_INCREMENT +-- + +-- +-- 使用表AUTO_INCREMENT `lsky_config` +-- +ALTER TABLE `lsky_config` + MODIFY `id` smallint(6) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=42; + +-- +-- 使用表AUTO_INCREMENT `lsky_images` +-- +ALTER TABLE `lsky_images` + MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID'; + +-- +-- 使用表AUTO_INCREMENT `lsky_users` +-- +ALTER TABLE `lsky_users` + MODIFY `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 00000000..220826a5 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,8 @@ + + Options +FollowSymlinks -Multiviews + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L] + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..cf80eb9e Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.php b/public/index.php new file mode 100644 index 00000000..c981138c --- /dev/null +++ b/public/index.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- + +// [ 应用入口文件 ] +namespace think; + +// 根目录常量配置(和本文件同级) +define('ROOT_PATH', __DIR__ . DIRECTORY_SEPARATOR); + +// 加载基础文件 +require __DIR__ . '/../thinkphp/base.php'; + +// 执行应用并响应 +Container::get('app')->bind('index')->run()->send(); \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..eb053628 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/public/static/app/css/app.css b/public/static/app/css/app.css new file mode 100644 index 00000000..b3997af8 --- /dev/null +++ b/public/static/app/css/app.css @@ -0,0 +1,497 @@ +*, +*:before, +*:after { + box-sizing: border-box; +} +a { + color: #03A9F4; +} +a, +a:hover, +a:active, +a:focus { + text-decoration: none; +} +a:focus { + outline-style: none; +} +audio, +body, +caption, +div, +footer, +form, +h1, +h2, +h3, +h4, +h5, +h6, +header, +html, +iframe, +label, +legend, +li, +main, +mark, +menu, +nav, +ol, +p, +section, +span, +summary, +table, +textarea, +time, +ul, +video { + padding: 0; + margin: 0; + border: 0; + outline: 0 none; +} +html { + position: relative; + min-height: 100%; +} +html, +body { + width: 100%; + margin: 0; + padding: 0; +} +body { + margin-bottom: 60px; + overflow-x: hidden; +} +.mdui-drawer-body-left footer { + width: calc(100% - 240px); +} +main { + position: relative; + top: 0; + left: 0; + right: 0; + margin: 1.7rem 0; +} +main .login-container, +main .register-container { + margin-top: 4rem; + margin-bottom: 4rem; +} +main .not-logged-in { + margin: 10rem 0; + text-align: center; +} +main .not-logged-in h2 { + margin-bottom: 2rem; +} +main .upload-container .title { + margin: 1rem 0; +} +main .upload-container .title h1 { + font-weight: 300; + font-size: 3rem; + text-shadow: -5px 5px 0 rgba(0, 0, 0, 0.1); +} +main .upload-container .title p { + margin-top: 1rem; + font-size: 1.3rem; + color: #999; +} +main .upload-container .success-info { + margin-top: 1rem; + width: 0; +} +main .upload-container .success-info .mdui-tab a { + text-transform: inherit; +} +main .upload-container .success-info ul { + list-style: none; +} +main .upload-container .success-info ul li { + margin-top: 0.5rem; + padding: 1rem; + border: 1px solid #dadada; + background-color: #f7f7f7; + font-size: 14px; + color: #555; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; + border-radius: 0; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +main .system-container .mdui-textfield-label { + pointer-events: inherit; +} +main .images-container #info .mdui-dialog-content { + word-break: break-all; + word-wrap: break-word; +} +main .images-container #info .mdui-btn-group .mdui-btn { + margin-left: 0; + min-width: inherit; +} +main .images-container #info img.qrcode { + text-align: center; +} +main .images-container #info .mdui-dialog-content { + padding-bottom: 0; +} +main .images-container #info table { + width: 100%; +} +main .images-container #info table tbody tr td[align="right"] { + width: 50px; + color: #998; +} +main .images-container .images-box .item { + position: relative; + padding: 2rem 0.4rem 0.4rem; + text-align: center; + width: 100%; + height: auto; + transition: all 0.1s; + border: 1px solid transparent; + margin-top: 1rem; +} +main .images-container .images-box .item i.iconfont { + display: none; + position: absolute; + cursor: pointer; + font-size: 19px; +} +main .images-container .images-box .item i.icon-choice { + top: 5px; + left: 5px; + color: #cecece; +} +main .images-container .images-box .item i.icon-choice:hover { + color: #999; +} +main .images-container .images-box .item i.icon-info { + top: 5px; + right: 5px; + color: #129cff; +} +main .images-container .images-box .item:hover { + background-color: #ebebeb; +} +main .images-container .images-box .item.choice { + background: rgba(204, 232, 255, 0.5); + border: 1px solid rgba(153, 209, 255, 0.57); +} +main .images-container .images-box .item.choice i.icon-choice { + display: block; + color: #3b8cff; +} +main .images-container .images-box .item:hover i.icon-choice, +main .images-container .images-box .item.choice i.icon-choice, +main .images-container .images-box .item:hover i.icon-info, +main .images-container .images-box .item.choice i.icon-info { + display: block; +} +main .images-container .images-box .item .info { + cursor: pointer; + height: 90px; + overflow: hidden; + display: block; + text-align: center; + vertical-align: middle; +} +main .images-container .images-box .item .info img { + max-height: 100%; + max-width: 100%; + border-radius: 0; + position: relative; + top: 50%; + transform: translateY(-50%); +} +main .images-container .images-box .item .info.image img { + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); +} +main .images-container .images-box .item p.name { + color: #555; + cursor: default; + text-align: center; + word-break: break-all; + font-size: 12px; + margin: 0.3rem auto; + line-height: 1.5em; + padding-bottom: 5px; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; +} +main .update-container .item { + margin-top: 1rem; +} +main .update-container .item h4 { + font-size: 1.1rem; + margin-bottom: 0.5rem; + color: #555; +} +main .update-container .item h4 small { + color: #777777; +} +main .update-container .item .info { + margin-left: 0.8rem; +} +main .update-container .item .info p { + color: #777; + margin-bottom: 0.3em; +} +main .update-container p { + color: #444; +} +main .mdui-textfield .captcha { + position: absolute; + top: 34px; + right: 0; + bottom: 0; + width: 140px; + cursor: pointer; +} +.file-input .file-preview { + border-radius: 0; + border: none; + box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.13); +} +.file-input .file-preview .fileinput-remove { + top: 6px; + right: 6px; +} +.file-input .file-preview .file-preview-thumbnails .krajee-default.file-preview-frame { + border: 0; + box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.13); +} +.file-input .file-preview .kv-fileinput-error ul { + list-style: none; +} +.file-input .file-caption-main .form-control { + box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.13); + border: none; + border-radius: 0; + padding: 7px 12px; +} +.file-input .file-caption-main .form-control:focus { + z-index: 0; +} +.file-input .file-caption-main .btn { + padding: 7px 12px; + border: 0; +} +.file-input .file-caption-main .btn:first-child { + border-left: 1px solid #e2e2e2; +} +.file-input .file-caption-main .btn:last-child { + margin-right: -1px; +} +.file-input .file-caption-main .btn-file { + border-color: #2383eb; + background-color: #2383eb; + border-radius: 0; +} +.file-input .file-caption-main .input-group-append { + box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.13); +} +.file-zoom-fullscreen.modal { + z-index: 999999; +} +.mdui-tab-scrollable { + padding-left: 0; +} +.panel { + padding: 0; + overflow: hidden; + white-space: normal; + word-break: break-all; + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.13); + border-radius: 0.5rem; +} +.panel .panel-header { + padding: 1rem; +} +.panel .panel-body { + border-top: 1px solid #f1f1f1; + border-bottom: 1px solid #f1f1f1; + padding: 1rem; +} +.panel .panel-body-box { + border-top: 1px solid #f1f1f1; + padding: 1rem; + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} +.panel .panel-footer { + padding: 1rem; +} +.error-box { + border: none; + margin: 0; +} +.none { + display: none; +} +#loading-container { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: none; +} +#loading-container .mask { + position: absolute; + width: 100%; + z-index: 9999; +} +#loading-container .loading { + position: absolute; + top: 5rem; + right: 1rem; + z-index: 10000; +} +.panel-box { + overflow: hidden; + white-space: normal; + word-break: break-all; + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.13); + border-radius: 0.5rem; + padding: 1rem; +} +#menu .quota-container { + position: absolute; + left: 50%; + bottom: 2rem; + text-align: left; + transform: translateX(-50%); +} +#menu .quota-container progress { + background-color: #f6f6f6; + display: inline; + height: 0.5rem; +} +#menu .quota-container progress::-webkit-progress-bar { + background-color: #e0eaf0; +} +#menu .quota-container progress::-webkit-progress-value { + background-color: #1abc9c; +} +#menu .quota-container progress::-moz-progress-bar { + background-color: #e0eaf0; +} +#menu .quota-container progress::-moz-progress-value { + background-color: #708d9b; +} +#search-form .search-input { + display: inline-block; + padding-bottom: 0.6rem; + width: 140px; +} +footer { + position: absolute; + bottom: 0; + width: 100%; + height: 60px; + line-height: 60px; + color: #999; + text-align: left; + background-color: #f5f5f5; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + text-decoration: none; + color: #00b5ad; + background-color: #fff; + border: 1px solid #ddd; + margin-left: -1px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; +} +.pagination > li > a:hover, +.pagination > li > a:focus, +.pagination > li > span:hover, +.pagination > li > span:focus { + z-index: 2; + color: #22ddde; + background-color: #eeeeee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > a:hover, +.pagination > .active > a:focus, +.pagination > .active > span, +.pagination > .active > span:hover, +.pagination > .active > span:focus { + z-index: 3; + color: #e4e4e4; + background-color: transparent; + border-color: #e4e4e4; + cursor: default; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777777; + background-color: #fff; + border-color: #ddd; + cursor: not-allowed; +} +@media (max-width: 768px) { + body { + margin-bottom: 90px; + } + .krajee-default.file-preview-frame { + width: 95%; + } + .krajee-default.file-preview-frame .kv-file-content { + width: 100%; + height: 100%; + } + #loading-container .loading { + top: 4.5rem; + } + main .images-container #info img.qrcode { + width: 150px; + } + footer { + height: auto; + width: 100% !important; + line-height: 1.666; + padding: 1rem 0; + } +} +/*# sourceMappingURL=app.css.map */ \ No newline at end of file diff --git a/public/static/app/css/app.css.map b/public/static/app/css/app.css.map new file mode 100644 index 00000000..19353984 --- /dev/null +++ b/public/static/app/css/app.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["app.less"],"names":[],"mappings":"AAAA;AAAG,CAAC;AAAS,CAAC;EACZ,sBAAA;;AAGF;EACE,cAAA;;AAGF;AAAG,CAAC;AAAQ,CAAC;AAAS,CAAC;EACrB,qBAAA;;AAGF,CAAC;EACC,mBAAA;;AAGF;AAAO;AAAM;AAAS;AAAK;AAAQ;AAAM;AAAI;AAAI;AAAI;AAAI;AAAI;AAAI;AAAQ;AAAM;AAAQ;AAAO;AAAQ;AAAI;AAAM;AAAM;AAAM;AAAK;AAAI;AAAG;AAAS;AAAM;AAAS;AAAO;AAAU;AAAM;AAAI;EACzL,UAAA;EACA,SAAA;EACA,SAAA;EACA,eAAA;;AAGF;EACE,kBAAA;EACA,gBAAA;;AAGF;AAAM;EACJ,WAAA;EACA,SAAA;EACA,UAAA;;AAGF;EACE,mBAAA;EACA,kBAAA;;AAGF,sBACE;EACE,OAAO,kBAAP;;AAIJ;EACE,kBAAA;EACA,MAAA;EACA,OAAA;EACA,QAAA;EACA,gBAAA;;AALF,IAME;AANF,IAMoB;EAChB,gBAAA;EACA,mBAAA;;AARJ,IAUE;EACE,eAAA;EACA,kBAAA;;AAZJ,IAUE,eAGE;EACE,mBAAA;;AAdN,IAiBE,kBACE;EACE,cAAA;;AAnBN,IAiBE,kBACE,OAEE;EACE,gBAAA;EACA,eAAA;EACA,0CAAA;;AAvBR,IAiBE,kBACE,OAOE;EACE,gBAAA;EACA,iBAAA;EACA,WAAA;;AA5BR,IAiBE,kBAcE;EACE,gBAAA;EACA,QAAA;;AAjCN,IAiBE,kBAcE,cAGE,UAAU;EACR,uBAAA;;AAnCR,IAiBE,kBAcE,cAME;EACE,gBAAA;;AAtCR,IAiBE,kBAcE,cAME,GAEE;EACE,kBAAA;EACA,aAAA;EACA,yBAAA;EACA,yBAAA;EACA,eAAA;EACA,WAAA;EACA,qBAAA;EACA,qBAAA;EACA,qBAAA;EACA,gBAAA;EACA,sCAAsC,wBAAtC;;AAlDV,IAwDE,kBACE;EACE,uBAAA;;AA1DN,IA8DE,kBACE,MACE;EACE,qBAAA;EACA,qBAAA;;AAlER,IA8DE,kBACE,MAKE,gBACE;EACE,cAAA;EACA,kBAAA;;AAvEV,IA8DE,kBACE,MAWE,IAAG;EACD,kBAAA;;AA3ER,IA8DE,kBACE,MAcE;EACE,iBAAA;;AA9ER,IA8DE,kBACE,MAiBE;EACE,WAAA;;AAIM,IAvBZ,kBACE,MAiBE,MAEE,MACE,GACE,GACG;EACC,WAAA;EACA,WAAA;;AAvFhB,IA8DE,kBAkCE,YACE;EACE,kBAAA;EACA,2BAAA;EACA,kBAAA;EACA,WAAA;EACA,YAAA;EACA,oBAAA;EACA,6BAAA;EACA,gBAAA;;AAzGR,IA8DE,kBAkCE,YACE,MASE,EAAC;EACC,aAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;;AA9GV,IA8DE,kBAkCE,YACE,MAgBE,EAAC;EACC,QAAA;EACA,SAAA;EACA,cAAA;;AACA,IAvDR,kBAkCE,YACE,MAgBE,EAAC,YAIE;EACC,WAAA;;AAtHZ,IA8DE,kBAkCE,YACE,MAyBE,EAAC;EACC,QAAA;EACA,UAAA;EACA,cAAA;;AAEF,IAjEN,kBAkCE,YACE,MA8BG;EACC,yBAAA;;AAEF,IApEN,kBAkCE,YACE,MAiCG;EACC,oCAAA;EACA,2CAAA;;AAFF,IApEN,kBAkCE,YACE,MAiCG,OAGC,EAAC;EACC,cAAA;EACA,cAAA;;AAGJ,IA5EN,kBAkCE,YACE,MAyCG,MACC,EAAC;AADM,IA5Ef,kBAkCE,YACE,MAyCY,OACR,EAAC;AADH,IA5EN,kBAkCE,YACE,MAyCG,MACgB,EAAC;AADT,IA5Ef,kBAkCE,YACE,MAyCY,OACO,EAAC;EACd,cAAA;;AA5IZ,IA8DE,kBAkCE,YACE,MA8CE;EACE,eAAA;EACA,YAAA;EACA,gBAAA;EACA,cAAA;EACA,kBAAA;EACA,sBAAA;;AArJV,IA8DE,kBAkCE,YACE,MA8CE,MAOE;EACE,gBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;EACA,QAAA;EACA,WAAW,gBAAX;;AAEF,IAhGR,kBAkCE,YACE,MA8CE,MAeG,MACC;EACE,kDAAA;EACA,+CAAA;EACA,0CAAA;;AAlKd,IA8DE,kBAkCE,YACE,MAqEE,EAAC;EACC,WAAA;EACA,eAAA;EACA,kBAAA;EACA,qBAAA;EACA,eAAA;EACA,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,gBAAA;EACA,oBAAA;EACA,4BAAA;EACA,qBAAA;;AAlLV,IAwLE,kBACE;EACE,gBAAA;;AA1LN,IAwLE,kBACE,MAEE;EACE,iBAAA;EACA,qBAAA;EACA,WAAA;;AA9LR,IAwLE,kBACE,MAEE,GAIE;EACE,cAAA;;AAhMV,IAwLE,kBACE,MAUE;EACE,mBAAA;;AApMR,IAwLE,kBACE,MAUE,MAEE;EACE,WAAA;EACA,oBAAA;;AAvMV,IAwLE,kBAmBE;EACE,WAAA;;AA5MN,IA+ME,gBACE;EACE,kBAAA;EACA,SAAA;EACA,QAAA;EACA,SAAA;EACA,YAAA;EACA,eAAA;;AAMN,WAGE;EACE,gBAAA;EACA,YAAA;EACA,0CAAA;;AANJ,WAGE,cAIE;EACE,QAAA;EACA,UAAA;;AATN,WAGE,cAQE,yBACE,gBAAe;EACb,SAAA;EACA,0CAAA;;AAdR,WAGE,cAcE,oBACE;EACE,gBAAA;;AAnBR,WAuBE,mBACE;EACE,4CAAA;EACA,YAAA;EACA,gBAAA;EACA,iBAAA;;AACA,WANJ,mBACE,cAKG;EACC,UAAA;;AA9BR,WAuBE,mBAUE;EACE,iBAAA;EACA,SAAA;;AACA,WAbJ,mBAUE,KAGG;EACC,8BAAA;;AAEF,WAhBJ,mBAUE,KAMG;EACC,kBAAA;;AAxCR,WAuBE,mBAoBE;EACE,qBAAA;EACA,yBAAA;EACA,gBAAA;;AA9CN,WAuBE,mBAyBE;EACE,0CAAA;;AAMN,qBAAqB;EACnB,eAAA;;AAIF;EACE,eAAA;;AAGF;EAIE,UAAA;EACA,gBAAA;EACA,mBAAA;EACA,qBAAA;EACA,0CAAA;EACA,qBAAA;;AATF,MAUE;EACE,aAAA;;AAXJ,MAaE;EACE,6BAAA;EACA,gCAAA;EACA,aAAA;;AAhBJ,MAkBE;EACE,6BAAA;EACA,aAAA;EACA,iCAAA;EACA,kCAAA;;AAtBJ,MAwBE;EACE,aAAA;;AAIJ;EACE,YAAA;EACA,SAAA;;AAGF;EACE,aAAA;;AAGF;EACE,eAAA;EACA,MAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,aAAA;;AANF,kBAOE;EACE,kBAAA;EACA,WAAA;EACA,aAAA;;AAVJ,kBAYE;EACE,kBAAA;EACA,SAAA;EACA,WAAA;EACA,cAAA;;AAIJ;EACE,gBAAA;EACA,mBAAA;EACA,qBAAA;EACA,0CAAA;EACA,qBAAA;EACA,aAAA;;AAGF,KACE;EACE,kBAAA;EACA,SAAA;EACA,YAAA;EACA,gBAAA;EACA,WAAW,gBAAX;;AANJ,KACE,iBAME;EACE,yBAAA;EACA,eAAA;EACA,cAAA;;AAVN,KACE,iBAWE,SAAQ;EACN,yBAAA;;AAbN,KACE,iBAcE,SAAQ;EACN,yBAAA;;AAhBN,KACE,iBAiBE,SAAQ;EACN,yBAAA;;AAnBN,KACE,iBAoBE,SAAQ;EACN,yBAAA;;AAKN,YACE;EACE,qBAAA;EACA,sBAAA;EACA,YAAA;;AAIJ;EACE,kBAAA;EACA,SAAA;EACA,WAAA;EACA,YAAA;EACA,iBAAA;EACA,WAAA;EACA,gBAAA;EACA,yBAAA;;AAGF;EACE,qBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;;AAGF,WAAY;EACV,eAAA;;AAGF,WAAY,KAAK;AACjB,WAAY,KAAK;EACf,kBAAA;EACA,WAAA;EACA,qBAAA;EACA,cAAA;EACA,sBAAA;EACA,sBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;;AAGF,WAAY,KAAI,YAAa;AAC7B,WAAY,KAAI,YAAa;EAC3B,cAAA;EACA,8BAAA;EACA,2BAAA;;AAGF,WAAY,KAAI,WAAY;AAC5B,WAAY,KAAI,WAAY;EAC1B,+BAAA;EACA,4BAAA;;AAGF,WAAY,KAAK,IAAG;AACpB,WAAY,KAAK,IAAG;AACpB,WAAY,KAAK,OAAM;AACvB,WAAY,KAAK,OAAM;EACrB,UAAA;EACA,cAAA;EACA,yBAAA;EACA,kBAAA;;AAGF,WAAY,UAAU;AACtB,WAAY,UAAU,IAAG;AACzB,WAAY,UAAU,IAAG;AACzB,WAAY,UAAU;AACtB,WAAY,UAAU,OAAM;AAC5B,WAAY,UAAU,OAAM;EAC1B,UAAA;EACA,cAAA;EACA,6BAAA;EACA,qBAAA;EACA,eAAA;;AAGF,WAAY,YAAY;AACxB,WAAY,YAAY,OAAM;AAC9B,WAAY,YAAY,OAAM;AAC9B,WAAY,YAAY;AACxB,WAAY,YAAY,IAAG;AAC3B,WAAY,YAAY,IAAG;EACzB,cAAA;EACA,sBAAA;EACA,kBAAA;EACA,mBAAA;;AASF,QAA0B;EACxB;IACE,mBAAA;;EAGF,eAAe;IACb,UAAA;;EADF,eAAe,mBAEb;IACE,WAAA;IACA,YAAA;;EAIJ,kBACE;IACE,WAAA;;EAIJ,IAEE,kBACE,MACE,IAAG;IACD,YAAA;;EAMR;IACE,YAAA;IACA,sBAAA;IACA,kBAAA;IACA,eAAA","file":"app.css"} \ No newline at end of file diff --git a/public/static/app/css/app.less b/public/static/app/css/app.less new file mode 100644 index 00000000..15067dbe --- /dev/null +++ b/public/static/app/css/app.less @@ -0,0 +1,562 @@ +*, *:before, *:after { + box-sizing: border-box; +} + +a { + color: #03A9F4; +} + +a, a:hover, a:active, a:focus { + text-decoration: none; +} + +a:focus { + outline-style: none; +} + +audio, body, caption, div, footer, form, h1, h2, h3, h4, h5, h6, header, html, iframe, label, legend, li, main, mark, menu, nav, ol, p, section, span, summary, table, textarea, time, ul, video { + padding: 0; + margin: 0; + border: 0; + outline: 0 none; +} + +html { + position: relative; + min-height: 100%; +} + +html, body { + width: 100%; + margin: 0; + padding: 0; +} + +body { + margin-bottom: 60px; + overflow-x: hidden; +} + +.mdui-drawer-body-left { + footer { + width: calc(100% - 240px); + } +} + +main { + position: relative; + top: 0; + left: 0; + right: 0; + margin: 1.7rem 0; + .login-container, .register-container { + margin-top: 4rem; + margin-bottom: 4rem; + } + .not-logged-in { + margin: 10rem 0; + text-align: center; + h2 { + margin-bottom: 2rem; + } + } + .upload-container { + .title { + margin: 1rem 0; + h1 { + font-weight: 300; + font-size: 3rem; + text-shadow: -5px 5px 0 rgba(0, 0, 0, .1); + } + p { + margin-top: 1rem; + font-size: 1.3rem; + color: #999; + } + } + .success-info { + margin-top: 1rem; + width: 0; + .mdui-tab a { + text-transform: inherit; + } + ul { + list-style: none; + li { + margin-top: 0.5rem; + padding: 1rem; + border: 1px solid #dadada; + background-color: #f7f7f7; + font-size: 14px; + color: #555; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; + border-radius: 0; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + } + } + } + } + // 系统设置 + .system-container { + .mdui-textfield-label { + pointer-events: inherit; + } + } + // 我的图片 + .images-container { + #info { + .mdui-dialog-content { + word-break: break-all; + word-wrap: break-word; + } + .mdui-btn-group { + .mdui-btn { + margin-left: 0; + min-width: inherit; + } + } + img.qrcode { + text-align: center; + } + .mdui-dialog-content { + padding-bottom: 0; + } + table { + width: 100%; + tbody { + tr { + td { + &[align="right"] { + width: 50px; + color: #998; + } + } + } + } + } + } + .screen-box { + } + .images-box { + .item { + position: relative; + padding: 2rem .4rem .4rem; + text-align: center; + width: 100%; + height: auto; + transition: all .1s; + border: 1px solid transparent; + margin-top: 1rem; + i.iconfont { + display: none; + position: absolute; + cursor: pointer; + font-size: 19px; + } + // 选择 + i.icon-choice { + top: 5px; + left: 5px; + color: #cecece; + &:hover { + color: #999; + } + } + // 信息 + i.icon-info { + top: 5px; + right: 5px; + color: #129cff; + } + &:hover { + background-color: #ebebeb; + } + &.choice { + background: rgba(204, 232, 255, 0.5); + border: 1px solid rgba(153, 209, 255, 0.57); + i.icon-choice { + display: block; + color: #3b8cff; + } + } + &:hover, &.choice { + i.icon-choice, i.icon-info { + display: block; + } + } + .info { + cursor: pointer; + height: 90px; + overflow: hidden; + display: block; + text-align: center; + vertical-align: middle; + img { + max-height: 100%; + max-width: 100%; + border-radius: 0; + position: relative; + top: 50%; + transform: translateY(-50%); + } + &.image { + img { + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + } + } + } + p.name { + color: #555; + cursor: default; + text-align: center; + word-break: break-all; + font-size: 12px; + margin: .3rem auto; + line-height: 1.5em; + padding-bottom: 5px; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + } + } + } + } + // 系统更新 + .update-container { + .item { + margin-top: 1rem; + h4 { + font-size: 1.1rem; + margin-bottom: .5rem; + color: #555; + small { + color: #777777; + } + } + .info { + margin-left: .8rem; + p { + color: #777; + margin-bottom: .3em; + } + } + } + p { + color: #444; + } + } + .mdui-textfield { + .captcha { + position: absolute; + top: 34px; + right: 0; + bottom: 0; + width: 140px; + cursor: pointer; + } + } +} + +// 重写fileinput插件样式 +.file-input { + @box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.13); + @form-control-padding: 7px 12px; + .file-preview { + border-radius: 0; + border: none; + box-shadow: @box-shadow; + .fileinput-remove { + top: 6px; + right: 6px; + } + .file-preview-thumbnails { + .krajee-default.file-preview-frame { + border: 0; + box-shadow: @box-shadow; + } + } + .kv-fileinput-error { + ul { + list-style: none; + } + } + } + .file-caption-main { + .form-control { + box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.13); + border: none; + border-radius: 0; + padding: @form-control-padding; + &:focus { + z-index: 0; + } + } + .btn { + padding: @form-control-padding; + border: 0; + &:first-child { + border-left: 1px solid #e2e2e2; + } + &:last-child { + margin-right: -1px; + } + } + .btn-file { + border-color: #2383eb; + background-color: #2383eb; + border-radius: 0; + } + .input-group-append { + box-shadow: @box-shadow; + } + } +} + +// 摸态框全屏 +.file-zoom-fullscreen.modal { + z-index: 999999; +} + +// mdui +.mdui-tab-scrollable { + padding-left: 0; +} + +.panel { + @border-radius: .5rem; + @padding: 1rem; + @border-color: #f1f1f1; + padding: 0; + overflow: hidden; + white-space: normal; + word-break: break-all; + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.13); + border-radius: @border-radius; + .panel-header { + padding: @padding; + } + .panel-body { + border-top: 1px solid @border-color; + border-bottom: 1px solid @border-color; + padding: @padding; + } + .panel-body-box { + border-top: 1px solid @border-color; + padding: @padding; + border-bottom-left-radius: @border-radius; + border-bottom-right-radius: @border-radius; + } + .panel-footer { + padding: @padding; + } +} + +.error-box { + border: none; + margin: 0; +} + +.none { + display: none; +} + +#loading-container { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: none; + .mask { + position: absolute; + width: 100%; + z-index: 9999; + } + .loading { + position: absolute; + top: 5rem; + right: 1rem; + z-index: 10000; + } +} + +.panel-box { + overflow: hidden; + white-space: normal; + word-break: break-all; + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.13); + border-radius: .5rem; + padding: 1rem; +} + +#menu { + .quota-container { + position: absolute; + left: 50%; + bottom: 2rem; + text-align: left; + transform: translateX(-50%); + progress { + background-color: #f6f6f6; + display: inline; + height: .5rem; + } + progress::-webkit-progress-bar { + background-color: #e0eaf0; + } + progress::-webkit-progress-value { + background-color: #1abc9c; + } + progress::-moz-progress-bar { + background-color: #e0eaf0; + } + progress::-moz-progress-value { + background-color: #708d9b; + } + } +} + +#search-form { + .search-input { + display: inline-block; + padding-bottom: .6rem; + width: 140px; + } +} + +footer { + position: absolute; + bottom: 0; + width: 100%; + height: 60px; + line-height: 60px; + color: #999; + text-align: left; + background-color: #f5f5f5; +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} + +.pagination > li { + display: inline; +} + +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + text-decoration: none; + color: #00b5ad; + background-color: #fff; + border: 1px solid #ddd; + margin-left: -1px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} + +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; +} + +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; +} + +.pagination > li > a:hover, +.pagination > li > a:focus, +.pagination > li > span:hover, +.pagination > li > span:focus { + z-index: 2; + color: #22ddde; + background-color: #eeeeee; + border-color: #ddd; +} + +.pagination > .active > a, +.pagination > .active > a:hover, +.pagination > .active > a:focus, +.pagination > .active > span, +.pagination > .active > span:hover, +.pagination > .active > span:focus { + z-index: 3; + color: #e4e4e4; + background-color: transparent; + border-color: #e4e4e4; + cursor: default; +} + +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777777; + background-color: #fff; + border-color: #ddd; + cursor: not-allowed; +} + +@media (max-width: 1200px) { +} + +@media (max-width: 992px) { +} + +@media (max-width: 768px) { + body { + margin-bottom: 90px; + } + + .krajee-default.file-preview-frame { + width: 95%; + .kv-file-content { + width: 100%; + height: 100%; + } + } + + #loading-container { + .loading { + top: 4.5rem; + } + } + + main { + // 我的图片 + .images-container { + #info { + img.qrcode { + width: 150px; + } + } + } + } + + footer { + height: auto; + width: 100% !important; + line-height: 1.666; + padding: 1rem 0; + } +} + +@media (max-width: 576px) { + +} \ No newline at end of file diff --git a/public/static/app/iconfont/iconfont.css b/public/static/app/iconfont/iconfont.css new file mode 100644 index 00000000..4a2a5a66 --- /dev/null +++ b/public/static/app/iconfont/iconfont.css @@ -0,0 +1,25 @@ + +@font-face {font-family: "iconfont"; + src: url('iconfont.eot?t=1538632687575'); /* IE9*/ + src: url('iconfont.eot?t=1538632687575#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAZsAAsAAAAACUAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8hE3CY21hcAAAAYAAAABsAAABsuLGn6BnbHlmAAAB7AAAAmkAAALctxm+EGhlYWQAAARYAAAALwAAADYS1t3baGhlYQAABIgAAAAeAAAAJAfeA4lobXR4AAAEqAAAABEAAAAUFAMAAGxvY2EAAAS8AAAADAAAAAwB0gJKbWF4cAAABMgAAAAfAAAAIAESAJJuYW1lAAAE6AAAAUUAAAJtPlT+fXBvc3QAAAYwAAAAOQAAAEpzmDqXeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWScwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGBye8b0+z9zwv4EhhrmBoQEozAiSAwDuXwzGeJztkbsNgDAMRF8+IIQQa4SCIRiIikXZIikyRLBjCobgrBedT05jAwMQhF2I4C4cqlNS1/PA3PPIIf3MhMfnNaey1bs1+PpXTuas1Af568WM/Fr6e7xd1O0Zuu2cDL1I2QydqbeBfwDrxBnbeJw9Uj2IE1EQfvN+d7M/ySa7G+Pt5W+TDRwmd0k2WU7Jmhwcwh2Ipdx1tqeNNjY2gniFnaUH9trZCXJnp6WChdpYaCmIIFddNk4OFN6D7837Zr6ZjyGMkMUf9oSZxCHr5BEh7WaPxikdVKlr0zAJR3HUlKsQNqXrXwHP9QfjCQy9wTiOwo7N8yCVTf1ylZURIK3HOz1IUpakNEbU6dFOj6WQxEkqhli1XIWyr6oUsxT90t7qB0F/a2e2EQQbs/bNaaFib5qOY27alcLUchzrM1OGLalg0nU1Lpi9slb3ggvbXX8yTYctoWmKR8PEKxQVUN9UOY3JXE5nSjKer7eijlNeawW6WxQcW2UUDEsrBf3ZztZSEpX7wYfJUnHiVMx0qZhSu+J8tT3XsLW8pl3s9y55hWarWRCMcSaVpnE1Ha7WnLxlFF3PLHqG8Dwcx14pO6parVzuBnllFD2zMglqdW4ULadkoNFLr1+zd+waIkUKhMAo9EpO6JQG+Krjpd29vb3uG8hlp9lhdgo5qu/vZ0/ByX7RF/9icB9y57XeshM2IyZpkhBr2aDQWnQaTS+BjNYhiq/CeFCDZOzXoAqqD4uTOefzk+O5EPPj39lHbskjIdrClgdn3EYAgs3w6z8te4UkEEfIaAtxcIaESFiCEIr6Z+whXZBVMkL1xnJJhqOGcho4lF9u4KYMB6ifOHEEGFjiGHsKpfLOychhd+fvlb4Nzx5nP7eZAbu6ZPS7BD3rUYuzQzxMWDT7pjQwIaQLXX16fj3bfvkjR29JDW4zSR8oPdvlFtA73OT3GFg3dBlZaA/5CxEHgD4AAAB4nGNgZGBgAOKObMWYeH6brwzcLAwgcP32mfcI+v9OFgZmdyCXg4EJJAoASfcMCwB4nGNgZGBgbvjfwBDDwswABCwMDIwMqIAVAEdWAnEAAHicY2FgYGBBxswMDAABDQAYAAAAAAAAAAC2ANwBHAFueJxjYGRgYGBlaGNgZgABJiDmAkIGhv9gPgMAFiwBpQB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxjYGKAAC4G7ICVkYmRmZGFkZWRjYGjKDU9s7gktYglMy8tny05Iz8zOZU1Jz89M4+BAQCf6wnXAAAA') format('woff'), + url('iconfont.ttf?t=1538632687575') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ + url('iconfont.svg?t=1538632687575#iconfont') format('svg'); /* iOS 4.1- */ +} + +.iconfont { + font-family:"iconfont" !important; + font-size:16px; + font-style:normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-register:before { content: "\e60e"; } + +.icon-info:before { content: "\ebcf"; } + +.icon-choice:before { content: "\e627"; } + +.icon-login:before { content: "\e728"; } + diff --git a/public/static/app/iconfont/iconfont.eot b/public/static/app/iconfont/iconfont.eot new file mode 100644 index 00000000..9f06ecd5 Binary files /dev/null and b/public/static/app/iconfont/iconfont.eot differ diff --git a/public/static/app/iconfont/iconfont.js b/public/static/app/iconfont/iconfont.js new file mode 100644 index 00000000..c5cc4b3c --- /dev/null +++ b/public/static/app/iconfont/iconfont.js @@ -0,0 +1 @@ +(function(window){var svgSprite='';var script=function(){var scripts=document.getElementsByTagName("script");return scripts[scripts.length-1]}();var shouldInjectCss=script.getAttribute("data-injectcss");var ready=function(fn){if(document.addEventListener){if(~["complete","loaded","interactive"].indexOf(document.readyState)){setTimeout(fn,0)}else{var loadFn=function(){document.removeEventListener("DOMContentLoaded",loadFn,false);fn()};document.addEventListener("DOMContentLoaded",loadFn,false)}}else if(document.attachEvent){IEContentLoaded(window,fn)}function IEContentLoaded(w,fn){var d=w.document,done=false,init=function(){if(!done){done=true;fn()}};var polling=function(){try{d.documentElement.doScroll("left")}catch(e){setTimeout(polling,50);return}init()};polling();d.onreadystatechange=function(){if(d.readyState=="complete"){d.onreadystatechange=null;init()}}}};var before=function(el,target){target.parentNode.insertBefore(el,target)};var prepend=function(el,target){if(target.firstChild){before(el,target.firstChild)}else{target.appendChild(el)}};function appendSvg(){var div,svg;div=document.createElement("div");div.innerHTML=svgSprite;svgSprite=null;svg=div.getElementsByTagName("svg")[0];if(svg){svg.setAttribute("aria-hidden","true");svg.style.position="absolute";svg.style.width=0;svg.style.height=0;svg.style.overflow="hidden";prepend(svg,document.body)}}if(shouldInjectCss&&!window.__iconfont__svg__cssinject__){window.__iconfont__svg__cssinject__=true;try{document.write("")}catch(e){console&&console.log(e)}}ready(appendSvg)})(window) \ No newline at end of file diff --git a/public/static/app/iconfont/iconfont.svg b/public/static/app/iconfont/iconfont.svg new file mode 100644 index 00000000..71497d4f --- /dev/null +++ b/public/static/app/iconfont/iconfont.svg @@ -0,0 +1,38 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/app/iconfont/iconfont.ttf b/public/static/app/iconfont/iconfont.ttf new file mode 100644 index 00000000..0613348a Binary files /dev/null and b/public/static/app/iconfont/iconfont.ttf differ diff --git a/public/static/app/iconfont/iconfont.woff b/public/static/app/iconfont/iconfont.woff new file mode 100644 index 00000000..39f9607c Binary files /dev/null and b/public/static/app/iconfont/iconfont.woff differ diff --git a/public/static/app/images/loading.jpg b/public/static/app/images/loading.jpg new file mode 100644 index 00000000..6ada6ce2 Binary files /dev/null and b/public/static/app/images/loading.jpg differ diff --git a/public/static/app/images/logo.png b/public/static/app/images/logo.png new file mode 100644 index 00000000..49ec568d Binary files /dev/null and b/public/static/app/images/logo.png differ diff --git a/public/static/app/js/app.js b/public/static/app/js/app.js new file mode 100644 index 00000000..4baf48f2 --- /dev/null +++ b/public/static/app/js/app.js @@ -0,0 +1,118 @@ +var app = { + /** + * ajax + * @param url + * @param data + * @param success + * @param error + */ + ajax: function (url, data, success, error) { + var loading = $('#loading-container'); + $.ajax({ + url: url, + type: 'post', + data: data, + dataType: 'json', + beforeSend: function () { + loading.fadeIn(500); + }, + success: success, + error: error, + complete: function () { + loading.fadeOut(100); + } + }); + }, + /** + * 执行请求 + * @param url 请求地址 + * @param data 数据 + * @param success 后端返回成功后执行的回调 + * @param error 后端返回失败后执行的回调 + * @returns {*|void} + */ + request: function (url, data, success, error) { + return app.ajax(url, data, function (response) { + mdui.snackbar({ + message: "" + (response.code ? '' : '') + "; " + response.msg, + position: 'right-top', + timeout: response.code ? 1000 : 2000, + onClose: function () { + if (response.code) { + success && success(); + } else { + error && error(); + } + } + }); + }); + }, + /** + * Msg + * @param bool + * @param msg + * @param callback + */ + msg: function (bool, msg, callback) { + mdui.snackbar({ + message: "" + (bool ? '' : '') + "; " + msg, + position: 'right-top', + timeout: bool ? 1000 : 2000, + onClose: function () { + if (bool) callback && callback(); + } + }); + }, + cookie: { + /** + * 设置cookie + * @param key cookie名称 + * @param val cookie值 + * @param time 过期时间(天) + * @param path cookie路径 + */ + set: function (key, val, time, path) { + var date = new Date(); + var expiresDays = time; + date.setTime(date.getTime() + expiresDays * 24 * 3600 * 1000); + document.cookie = key + "=" + val + ";expires=" + date.toGMTString() + (path ? (";path=" + path) : ''); + }, + /** + * 获取cookie + * @param key cookie名称 + * @returns {*} + */ + get: function (key) { + var getCookie = document.cookie.replace(/[ ]/g, ""); + var arrCookie = getCookie.split(";"); + var tips; + for (var i = 0; i < arrCookie.length; i++) { + var arr = arrCookie[i].split("="); + if (key === arr[0]) { + tips = arr[1]; + break; + } + } + return tips; + }, + /** + * 删除cookie + * @param key + */ + delete: function (key) { + var date = new Date(); + date.setTime(date.getTime() - 10000); + document.cookie = key + "=v; expires =" + date.toGMTString(); + } + }, + /** + * 字节换算 + * @param bytes + * @returns {string} + */ + bytesToSize: function (bytes) { + if (bytes === 0) return '0 B'; + var k = 1024, sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]; + } +}; \ No newline at end of file diff --git a/public/static/bootstrap-fileinput/4.5.1/css/fileinput.min.css b/public/static/bootstrap-fileinput/4.5.1/css/fileinput.min.css new file mode 100644 index 00000000..95eb90a0 --- /dev/null +++ b/public/static/bootstrap-fileinput/4.5.1/css/fileinput.min.css @@ -0,0 +1,12 @@ +/*! + * bootstrap-fileinput v4.5.1 + * http://plugins.krajee.com/file-input + * + * Krajee default styling for bootstrap-fileinput. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD 3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */.btn-file input[type=file],.file-caption-icon,.file-no-browse,.file-preview .fileinput-remove,.file-zoom-dialog .btn-navigate,.file-zoom-dialog .floating-buttons,.krajee-default .file-thumb-progress{position:absolute}.file-loading input[type=file],input[type=file].file-loading{width:0;height:0}.file-no-browse{left:50%;bottom:20%;width:1px;height:1px;font-size:0;opacity:0;border:none;background:0 0;outline:0;box-shadow:none}.file-caption-icon,.file-input-ajax-new .fileinput-remove-button,.file-input-ajax-new .fileinput-upload-button,.file-input-ajax-new .no-browse .input-group-btn,.file-input-new .close,.file-input-new .file-preview,.file-input-new .fileinput-remove-button,.file-input-new .fileinput-upload-button,.file-input-new .glyphicon-file,.file-input-new .no-browse .input-group-btn,.file-zoom-dialog .modal-header:after,.file-zoom-dialog .modal-header:before,.hide-content .kv-file-content,.kv-hidden{display:none}.btn-file,.file-caption,.file-input,.file-loading:before,.file-preview,.file-zoom-dialog .modal-dialog,.krajee-default .file-thumbnail-footer,.krajee-default.file-preview-frame{position:relative}.file-error-message pre,.file-error-message ul,.krajee-default .file-actions,.krajee-default .file-other-error{text-align:left}.file-error-message pre,.file-error-message ul{margin:0}.krajee-default .file-drag-handle,.krajee-default .file-upload-indicator{float:left;margin:5px 0 -5px;width:16px;height:16px}.krajee-default .file-thumb-progress .progress,.krajee-default .file-thumb-progress .progress-bar{height:11px;font-family:Verdana,Helvetica,sans-serif;font-size:9px}.krajee-default .file-caption-info,.krajee-default .file-size-info{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:160px;height:15px;margin:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-image,.file-zoom-content>.file-object.type-video{max-width:100%;max-height:100%;width:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-video{height:100%}.file-zoom-content>.file-object.type-default,.file-zoom-content>.file-object.type-html,.file-zoom-content>.file-object.type-pdf,.file-zoom-content>.file-object.type-text{width:100%}.file-loading:before{content:" Loading...";display:inline-block;padding-left:20px;line-height:16px;font-size:13px;font-variant:small-caps;color:#999;background:url(../img/loading.gif) top left no-repeat}.file-object{margin:0 0 -5px;padding:0}.btn-file{overflow:hidden}.btn-file input[type=file]{top:0;left:0;min-width:100%;min-height:100%;text-align:right;opacity:0;background:none;cursor:inherit;display:block}.btn-file ::-ms-browse{font-size:10000px;width:100%;height:100%}.file-caption .file-caption-name{width:100%;margin:0;padding:0;box-shadow:none;border:none;background:0 0;outline:0}.file-caption.icon-visible .file-caption-icon{display:inline-block}.file-caption.icon-visible .file-caption-name{padding-left:15px}.file-caption-icon{left:8px}.file-error-message{color:#a94442;background-color:#f2dede;margin:5px;border:1px solid #ebccd1;border-radius:4px;padding:15px}.file-error-message pre{margin:5px 0}.file-caption-disabled{background-color:#eee;cursor:not-allowed;opacity:1}.file-preview{border-radius:5px;border:1px solid #ddd;padding:8px;width:100%;margin-bottom:5px}.file-preview .btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.file-preview .fileinput-remove{top:1px;right:1px;line-height:10px}.file-preview .clickable{cursor:pointer}.file-preview-image{font:40px Impact,Charcoal,sans-serif;color:green}.krajee-default.file-preview-frame{margin:8px;border:1px solid rgba(0,0,0,.2);box-shadow:0 0 10px 0 rgba(0,0,0,.2);padding:6px;float:left;text-align:center}.krajee-default.file-preview-frame .kv-file-content{width:213px;height:160px}.krajee-default.file-preview-frame .kv-file-content.kv-pdf-rendered{width:400px}.krajee-default.file-preview-frame .file-thumbnail-footer{height:70px}.krajee-default.file-preview-frame:not(.file-preview-error):hover{border:1px solid rgba(0,0,0,.3);box-shadow:0 0 10px 0 rgba(0,0,0,.4)}.krajee-default .file-preview-text{display:block;color:#428bca;border:1px solid #ddd;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;outline:0;padding:8px;resize:none}.krajee-default .file-preview-html{border:1px solid #ddd;padding:8px;overflow:auto}.krajee-default .file-other-icon{font-size:6em}.krajee-default .file-footer-buttons{float:right}.krajee-default .file-footer-caption{display:block;text-align:center;padding-top:4px;font-size:11px;color:#777;margin-bottom:15px}.krajee-default .file-preview-error{opacity:.65;box-shadow:none}.krajee-default .file-thumb-progress{height:11px;top:37px;left:0;right:0}.krajee-default.kvsortable-ghost{background:#e1edf7;border:2px solid #a1abff}.krajee-default .file-preview-other:hover{opacity:.8}.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover{color:#000}.kv-upload-progress .progress{height:20px;margin:10px 0;overflow:hidden}.kv-upload-progress .progress-bar{height:20px;font-family:Verdana,Helvetica,sans-serif}.file-zoom-dialog .file-other-icon{font-size:22em;font-size:50vmin}.file-zoom-dialog .modal-dialog{width:auto}.file-zoom-dialog .modal-header{display:flex;align-items:center;justify-content:space-between}.file-zoom-dialog .btn-navigate{padding:0;margin:0;background:0 0;text-decoration:none;outline:0;opacity:.7;top:45%;font-size:4em;color:#1c94c4}.file-zoom-dialog .btn-navigate:not([disabled]):hover{outline:0;box-shadow:none;opacity:.6}.file-zoom-dialog .floating-buttons{top:5px;right:10px}.file-zoom-dialog .btn-navigate[disabled]{opacity:.3}.file-zoom-dialog .btn-prev{left:1px}.file-zoom-dialog .btn-next{right:1px}.file-zoom-dialog .kv-zoom-title{font-weight:300;color:#999;max-width:50%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-input-ajax-new .no-browse .form-control,.file-input-new .no-browse .form-control{border-top-right-radius:4px;border-bottom-right-radius:4px}.file-caption-main{width:100%}.file-thumb-loading{background:url(../img/loading.gif) center center no-repeat content-box!important}.file-drop-zone{border:1px dashed #aaa;border-radius:4px;height:100%;text-align:center;vertical-align:middle;margin:12px 15px 12px 12px;padding:5px}.file-drop-zone.clickable:hover{border:2px dashed #999}.file-drop-zone.clickable:focus{border:2px solid #5acde2}.file-drop-zone .file-preview-thumbnails{cursor:default}.file-drop-zone-title{color:#aaa;font-size:1.6em;padding:85px 10px;cursor:default}.file-highlighted{border:2px dashed #999!important;background-color:#eee}.file-uploading{background:url(../img/loading-sm.gif) center bottom 10px no-repeat;opacity:.65}.file-zoom-fullscreen .modal-dialog{min-width:100%;margin:0}.file-zoom-fullscreen .modal-content{border-radius:0;box-shadow:none;min-height:100vh}.file-zoom-fullscreen .modal-body{overflow-y:auto}.floating-buttons{z-index:3000}.floating-buttons .btn-kv{margin-left:3px;z-index:3000}.file-zoom-content{height:480px;text-align:center}.file-zoom-content .file-preview-image,.file-zoom-content .file-preview-video{max-height:100%}.file-zoom-content>.file-object.type-image{height:auto;min-height:inherit}.file-zoom-content>.file-object.type-audio{width:auto;height:30px}@media (min-width:576px){.file-zoom-dialog .modal-dialog{max-width:500px}}@media (min-width:992px){.file-zoom-dialog .modal-lg{max-width:800px}}@media (max-width:767px){.file-preview-thumbnails{display:flex;justify-content:center;align-items:center;flex-direction:column}.file-zoom-dialog .modal-header{flex-direction:column}}@media (max-width:350px){.krajee-default.file-preview-frame .kv-file-content{width:160px}}@media (max-width:420px){.krajee-default.file-preview-frame .kv-file-content.kv-pdf-rendered{width:100%}}.file-loading[dir=rtl]:before{background:url(../img/loading.gif) top right no-repeat;padding-left:0;padding-right:20px}.file-sortable .file-drag-handle{cursor:move;opacity:1}.file-sortable .file-drag-handle:hover{opacity:.7}.clickable .file-drop-zone-title{cursor:pointer}.kv-zoom-actions .btn-kv{margin-left:3px}.file-preview-initial.sortable-chosen{background-color:#d9edf7} diff --git a/public/static/bootstrap-fileinput/4.5.1/img/loading-sm.gif b/public/static/bootstrap-fileinput/4.5.1/img/loading-sm.gif new file mode 100644 index 00000000..44e3b7a0 Binary files /dev/null and b/public/static/bootstrap-fileinput/4.5.1/img/loading-sm.gif differ diff --git a/public/static/bootstrap-fileinput/4.5.1/img/loading.gif b/public/static/bootstrap-fileinput/4.5.1/img/loading.gif new file mode 100644 index 00000000..0ea146c0 Binary files /dev/null and b/public/static/bootstrap-fileinput/4.5.1/img/loading.gif differ diff --git a/public/static/bootstrap-fileinput/4.5.1/js/fileinput.min.js b/public/static/bootstrap-fileinput/4.5.1/js/fileinput.min.js new file mode 100644 index 00000000..6632e562 --- /dev/null +++ b/public/static/bootstrap-fileinput/4.5.1/js/fileinput.min.js @@ -0,0 +1,11 @@ +/*! + * bootstrap-fileinput v4.5.1 + * http://plugins.krajee.com/file-input + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2018, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD 3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +!function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof module&&module.exports?module.exports=e(require("jquery")):e(window.jQuery)}(function(e){"use strict";var t,i;e.fn.fileinputLocales={},e.fn.fileinputThemes={},String.prototype.setTokens=function(e){var t,i,a=this.toString();for(t in e)e.hasOwnProperty(t)&&(i=new RegExp("{"+t+"}","g"),a=a.replace(i,e[t]));return a},t={FRAMES:".kv-preview-thumb",SORT_CSS:"file-sortable",OBJECT_PARAMS:'\n\n\n\n\n\n',DEFAULT_PREVIEW:'
          \n{previewFileIcon}\n
          ',MODAL_ID:"kvFileinputModal",MODAL_EVENTS:["show","shown","hide","hidden","loaded"],objUrl:window.URL||window.webkitURL,compare:function(e,t,i){return void 0!==e&&(i?e===t:e.match(t))},isIE:function(e){if("Microsoft Internet Explorer"!==navigator.appName)return!1;if(10===e)return new RegExp("msie\\s"+e,"i").test(navigator.userAgent);var t,i=document.createElement("div");return i.innerHTML="\x3c!--[if IE "+e+"]> 0&&e[0].webkitGetAsEntry())for(t=0;t=0?atob(e.split(",")[1]):decodeURIComponent(e.split(",")[1]),a=new ArrayBuffer(i.length),r=new Uint8Array(a),n=0;n>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:s+=String.fromCharCode(i);break;case 12:case 13:a=n[o++],s+=String.fromCharCode((31&i)<<6|63&a);break;case 14:a=n[o++],r=n[o++],s+=String.fromCharCode((15&i)<<12|(63&a)<<6|(63&r)<<0)}return s},isHtml:function(e){var t=document.createElement("div");t.innerHTML=e;for(var i=t.childNodes,a=i.length;a--;)if(1===i[a].nodeType)return!0;return!1},isSvg:function(e){return e.match(/^\s*<\?xml/i)&&(e.match(//g,">").replace(/"/g,""").replace(/'/g,"'")},replaceTags:function(t,i){var a=t;return i?(e.each(i,function(e,t){"function"==typeof t&&(t=t()),a=a.split(e).join(t)}),a):a},cleanMemory:function(e){var i=e.is("img")?e.attr("src"):e.find("source").attr("src");t.objUrl.revokeObjectURL(i)},findFileName:function(e){var t=e.lastIndexOf("/");return-1===t&&(t=e.lastIndexOf("\\")),e.split(e.substring(t,t+1)).pop()},checkFullScreen:function(){return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement},toggleFullScreen:function(e){var i=document,a=i.documentElement;a&&e&&!t.checkFullScreen()?a.requestFullscreen?a.requestFullscreen():a.msRequestFullscreen?a.msRequestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen&&a.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT):i.exitFullscreen?i.exitFullscreen():i.msExitFullscreen?i.msExitFullscreen():i.mozCancelFullScreen?i.mozCancelFullScreen():i.webkitExitFullscreen&&i.webkitExitFullscreen()},moveArray:function(t,i,a,r){var n=e.extend(!0,[],t);if(r&&n.reverse(),a>=n.length)for(var s=a-n.length;1+s--;)n.push(void 0);return n.splice(a,0,n.splice(i,1)[0]),r&&n.reverse(),n},cleanZoomCache:function(e){var t=e.closest(".kv-zoom-cache-theme");t.length||(t=e.closest(".kv-zoom-cache")),t.remove()},closeButton:function(e){return''},getRotation:function(e){switch(e){case 2:return"rotateY(180deg)";case 3:return"rotate(180deg)";case 4:return"rotate(180deg) rotateY(180deg)";case 5:return"rotate(270deg) rotateY(180deg)";case 6:return"rotate(90deg)";case 7:return"rotate(90deg) rotateY(180deg)";case 8:return"rotate(270deg)";default:return""}},setTransform:function(e,t){e&&(e.style.transform=t,e.style.webkitTransform=t,e.style["-moz-transform"]=t,e.style["-ms-transform"]=t,e.style["-o-transform"]=t)},setImageOrientation:function(e,i,a){if(e&&e.length){var r="load.fileinputimageorient";e.off(r).on(r,function(){var r=e.get(0),n=i&&i.length?i.get(0):null,s=r.offsetHeight,o=r.offsetWidth,l=t.getRotation(a);if(e.data("orientation",a),n&&i.data("orientation",a),a<5)return t.setTransform(r,l),void t.setTransform(n,l);var d=Math.atan(o/s),c=Math.sqrt(Math.pow(s,2)+Math.pow(o,2)),h=c?s/Math.cos(Math.PI/2+d)/c:1,p=" scale("+Math.abs(h)+")";t.setTransform(r,l+p),t.setTransform(n,l+p)})}}},(i=function(i,a){this.$element=e(i),this.$parent=this.$element.parent(),this._validate()&&(this.isPreviewable=t.hasFileAPISupport(),this.isIE9=t.isIE(9),this.isIE10=t.isIE(10),(this.isPreviewable||this.isIE9)&&(this._init(a),this._listen()),this.$element.removeClass("file-loading"))}).prototype={constructor:i,_cleanup:function(){this.reader=null,this.formdata={},this.uploadCount=0,this.uploadStatus={},this.uploadLog=[],this.uploadAsyncCount=0,this.loadedImages=[],this.totalImagesCount=0,this.ajaxRequests=[],this.clearStack(),this.fileBatchCompleted=!0,this.isPreviewable||(this.showPreview=!1),this.isError=!1,this.ajaxAborted=!1,this.cancelling=!1},_init:function(i,a){var r,n,s,o,l=this,d=l.$element;l.options=i,e.each(i,function(e,i){switch(e){case"minFileCount":case"maxFileCount":case"minFileSize":case"maxFileSize":case"maxFilePreviewSize":case"resizeImageQuality":case"resizeIfSizeMoreThan":case"progressUploadThreshold":case"initialPreviewCount":case"zoomModalHeight":case"minImageHeight":case"maxImageHeight":case"minImageWidth":case"maxImageWidth":l[e]=t.getNum(i);break;default:l[e]=i}}),l.rtl&&(o=l.previewZoomButtonIcons.prev,l.previewZoomButtonIcons.prev=l.previewZoomButtonIcons.next,l.previewZoomButtonIcons.next=o),a||l._cleanup(),l.$form=d.closest("form"),l._initTemplateDefaults(),l.uploadFileAttr=t.isEmpty(d.attr("name"))?"file_data":d.attr("name"),s=l._getLayoutTemplate("progress"),l.progressTemplate=s.replace("{class}",l.progressClass),l.progressCompleteTemplate=s.replace("{class}",l.progressCompleteClass),l.progressErrorTemplate=s.replace("{class}",l.progressErrorClass),l.isDisabled=d.attr("disabled")||d.attr("readonly"),l.isDisabled&&d.attr("disabled",!0),l.isClickable=l.browseOnZoneClick&&l.showPreview&&(l.dropZoneEnabled||!t.isEmpty(l.defaultPreviewContent)),l.isAjaxUpload=t.hasFileUploadSupport()&&!t.isEmpty(l.uploadUrl),l.dropZoneEnabled=t.hasDragDropSupport()&&l.dropZoneEnabled,l.isAjaxUpload||(l.dropZoneEnabled=l.dropZoneEnabled&&t.canAssignFilesToInput()),l.slug="function"==typeof i.slugCallback?i.slugCallback:l._slugDefault,l.mainTemplate=l.showCaption?l._getLayoutTemplate("main1"):l._getLayoutTemplate("main2"),l.captionTemplate=l._getLayoutTemplate("caption"),l.previewGenericTemplate=l._getPreviewTemplate("generic"),!l.imageCanvas&&l.resizeImage&&(l.maxImageWidth||l.maxImageHeight)&&(l.imageCanvas=document.createElement("canvas"),l.imageCanvasContext=l.imageCanvas.getContext("2d")),t.isEmpty(d.attr("id"))&&d.attr("id",t.uniqId()),l.namespace=".fileinput_"+d.attr("id").replace(/-/g,"_"),void 0===l.$container?l.$container=l._createContainer():l._refreshContainer(),n=l.$container,l.$dropZone=n.find(".file-drop-zone"),l.$progress=n.find(".kv-upload-progress"),l.$btnUpload=n.find(".fileinput-upload"),l.$captionContainer=t.getElement(i,"elCaptionContainer",n.find(".file-caption")),l.$caption=t.getElement(i,"elCaptionText",n.find(".file-caption-name")),t.isEmpty(l.msgPlaceholder)||(r=d.attr("multiple")?l.filePlural:l.fileSingle,l.$caption.attr("placeholder",l.msgPlaceholder.replace("{files}",r))),l.$captionIcon=l.$captionContainer.find(".file-caption-icon"),l.$previewContainer=t.getElement(i,"elPreviewContainer",n.find(".file-preview")),l.$preview=t.getElement(i,"elPreviewImage",n.find(".file-preview-thumbnails")),l.$previewStatus=t.getElement(i,"elPreviewStatus",n.find(".file-preview-status")),l.$errorContainer=t.getElement(i,"elErrorContainer",l.$previewContainer.find(".kv-fileinput-error")),l._validateDisabled(),t.isEmpty(l.msgErrorClass)||t.addCss(l.$errorContainer,l.msgErrorClass),a?l._errorsExist()||l.$errorContainer.hide():(l.$errorContainer.hide(),l.previewInitId="preview-"+t.uniqId(),l._initPreviewCache(),l._initPreview(!0),l._initPreviewActions(),l.$parent.hasClass("file-loading")&&(l.$container.insertBefore(l.$parent),l.$parent.remove())),l._setFileDropZoneTitle(),d.attr("disabled")&&l.disable(),l._initZoom(),l.hideThumbnailContent&&t.addCss(l.$preview,"hide-content")},_initTemplateDefaults:function(){var i,a,r,n,s,o,l,d,c,h=this;i=t.closeButton("fileinput-remove"),a='',r='
          \n",s='\x3c!--suppress ALL --\x3e\n",l='\n',o='\n\n'+t.OBJECT_PARAMS+" "+t.DEFAULT_PREVIEW+"\n\n",d='
          \n'+t.DEFAULT_PREVIEW+"\n
          \n",c={width:"100%",height:"100%","min-height":"480px"},h._isPdfRendered()&&(l=h.pdfRendererTemplate.replace("{renderer}",h.pdfRendererUrl)),h.defaults={layoutTemplates:{main1:'{preview}\n
          \n
          \n {caption}\n
          \n {remove}\n {cancel}\n {upload}\n {browse}\n
          \n
          ',main2:'{preview}\n
          \n
          \n{remove}\n{cancel}\n{upload}\n{browse}\n',preview:'
          \n {close}
          \n
          \n
          \n
          \n
          \n
          \n
          ',close:i,fileIcon:'',caption:'
          \n \n \n
          ',modalMain:a,modal:'\n',progress:'
          \n
          \n {status}\n
          \n
          ',size:" ({sizeText})",footer:'',indicator:'
          {indicator}
          ',actions:'
          \n \n
          \n{drag}\n
          ',actionDelete:'\n',actionUpload:'',actionDownload:'{downloadIcon}',actionZoom:'',actionDrag:'{dragIcon}',btnDefault:'',btnLink:'{icon} {label}',btnBrowse:'
          {icon} {label}
          ',zoomCache:''},previewMarkupTags:{tagBefore1:'
          \n',tagBefore2:'
          \n',tagAfter:"
          {footer}\n
          \n"},previewContentTemplates:{generic:"{content}\n",html:'
          {data}
          \n',image:'{caption}\n',text:'\n',office:'',gdocs:'',video:n,audio:s,flash:'\n',object:o,pdf:l,other:d},allowedPreviewTypes:["image","html","text","video","audio","flash","pdf","object"],previewTemplates:{},previewSettings:{image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:{width:"213px",height:"160px"},text:{width:"213px",height:"160px"},office:{width:"213px",height:"160px"},gdocs:{width:"213px",height:"160px"},video:{width:"213px",height:"160px"},audio:{width:"100%",height:"30px"},flash:{width:"213px",height:"160px"},object:{width:"213px",height:"160px"},pdf:{width:"100%",height:"160px"},other:{width:"213px",height:"160px"}},previewSettingsSmall:{image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:{width:"100%",height:"160px"},text:{width:"100%",height:"160px"},office:{width:"100%",height:"160px"},gdocs:{width:"100%",height:"160px"},video:{width:"100%",height:"auto"},audio:{width:"100%",height:"30px"},flash:{width:"100%",height:"auto"},object:{width:"100%",height:"auto"},pdf:{width:"100%",height:"160px"},other:{width:"100%",height:"160px"}},previewZoomSettings:{image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:c,text:c,office:{width:"100%",height:"100%","max-width":"100%","min-height":"480px"},gdocs:{width:"100%",height:"100%","max-width":"100%","min-height":"480px"},video:{width:"auto",height:"100%","max-width":"100%"},audio:{width:"100%",height:"30px"},flash:{width:"auto",height:"480px"},object:{width:"auto",height:"100%","max-width":"100%","min-height":"480px"},pdf:c,other:{width:"auto",height:"100%","min-height":"480px"}},fileTypeSettings:{image:function(e,i){return t.compare(e,"image.*")&&!t.compare(e,/(tiff?|wmf)$/i)||t.compare(i,/\.(gif|png|jpe?g)$/i)},html:function(e,i){return t.compare(e,"text/html")||t.compare(i,/\.(htm|html)$/i)},office:function(e,i){return t.compare(e,/(word|excel|powerpoint|office)$/i)||t.compare(i,/\.(docx?|xlsx?|pptx?|pps|potx?)$/i)},gdocs:function(e,i){return t.compare(e,/(word|excel|powerpoint|office|iwork-pages|tiff?)$/i)||t.compare(i,/\.(docx?|xlsx?|pptx?|pps|potx?|rtf|ods|odt|pages|ai|dxf|ttf|tiff?|wmf|e?ps)$/i)},text:function(e,i){return t.compare(e,"text.*")||t.compare(i,/\.(xml|javascript)$/i)||t.compare(i,/\.(txt|md|csv|nfo|ini|json|php|js|css)$/i)},video:function(e,i){return t.compare(e,"video.*")&&(t.compare(e,/(ogg|mp4|mp?g|mov|webm|3gp)$/i)||t.compare(i,/\.(og?|mp4|webm|mp?g|mov|3gp)$/i))},audio:function(e,i){return t.compare(e,"audio.*")&&(t.compare(i,/(ogg|mp3|mp?g|wav)$/i)||t.compare(i,/\.(og?|mp3|mp?g|wav)$/i))},flash:function(e,i){return t.compare(e,"application/x-shockwave-flash",!0)||t.compare(i,/\.(swf)$/i)},pdf:function(e,i){return t.compare(e,"application/pdf",!0)||t.compare(i,/\.(pdf)$/i)},object:function(){return!0},other:function(){return!0}},fileActionSettings:{showRemove:!0,showUpload:!0,showDownload:!0,showZoom:!0,showDrag:!0,removeIcon:'',removeClass:"btn btn-sm btn-kv btn-default btn-outline-secondary",removeErrorClass:"btn btn-sm btn-kv btn-danger",removeTitle:"Remove file",uploadIcon:'',uploadClass:"btn btn-sm btn-kv btn-default btn-outline-secondary",uploadTitle:"Upload file",uploadRetryIcon:'',uploadRetryTitle:"Retry upload",downloadIcon:'',downloadClass:"btn btn-sm btn-kv btn-default btn-outline-secondary",downloadTitle:"Download file",zoomIcon:'',zoomClass:"btn btn-sm btn-kv btn-default btn-outline-secondary",zoomTitle:"View Details",dragIcon:'',dragClass:"text-info",dragTitle:"Move / Rearrange",dragSettings:{},indicatorNew:'',indicatorSuccess:'',indicatorError:'',indicatorLoading:'',indicatorNewTitle:"Not uploaded yet",indicatorSuccessTitle:"Uploaded",indicatorErrorTitle:"Upload Error",indicatorLoadingTitle:"Uploading ..."}},e.each(h.defaults,function(t,i){"allowedPreviewTypes"!==t?h[t]=e.extend(!0,{},i,h[t]):void 0===h.allowedPreviewTypes&&(h.allowedPreviewTypes=i)}),h._initPreviewTemplates()},_initPreviewTemplates:function(){var i,a=this,r=a.previewMarkupTags,n=r.tagAfter;e.each(a.previewContentTemplates,function(e,s){t.isEmpty(a.previewTemplates[e])&&(i=r.tagBefore2,"generic"!==e&&"image"!==e&&"html"!==e&&"text"!==e||(i=r.tagBefore1),a._isPdfRendered()&&"pdf"===e&&(i=i.replace("kv-file-content","kv-file-content kv-pdf-rendered")),a.previewTemplates[e]=i+s+n)})},_initPreviewCache:function(){var i=this;i.previewCache={data:{},init:function(){var e=i.initialPreview;e.length>0&&!t.isArray(e)&&(e=e.split(i.initialPreviewDelimiter)),i.previewCache.data={content:e,config:i.initialPreviewConfig,tags:i.initialPreviewThumbTags}},count:function(){return i.previewCache.data&&i.previewCache.data.content?i.previewCache.data.content.length:0},get:function(a,r){var n,s,o,l,d,c,h,p="init_"+a,u=i.previewCache.data,f=u.config[a],m=u.content[a],g=i.previewInitId+"-"+p,v=t.ifSet("previewAsData",f,i.initialPreviewAsData),w=function(e,a,r,n,s,o,l,d,c){return d=" file-preview-initial "+t.SORT_CSS+(d?" "+d:""),i._generatePreviewTemplate(e,a,r,n,s,!1,null,d,o,l,c)};return m?(r=void 0===r||r,o=t.ifSet("type",f,i.initialPreviewFileType||"generic"),d=t.ifSet("filename",f,t.ifSet("caption",f)),c=t.ifSet("filetype",f,o),l=i.previewCache.footer(a,r,f&&f.size||null),h=t.ifSet("frameClass",f),n=v?w(o,m,d,c,g,l,p,h):w("generic",m,d,c,g,l,p,h,o).setTokens({content:u.content[a]}),u.tags.length&&u.tags[a]&&(n=t.replaceTags(n,u.tags[a])),t.isEmpty(f)||t.isEmpty(f.frameAttr)||((s=e(document.createElement("div")).html(n)).find(".file-preview-initial").attr(f.frameAttr),n=s.html(),s.remove()),n):""},add:function(e,a,r,n){var s,o=i.previewCache.data;return t.isArray(e)||(e=e.split(i.initialPreviewDelimiter)),n?(s=o.content.push(e)-1,o.config[s]=a,o.tags[s]=r):(s=e.length-1,o.content=e,o.config=a,o.tags=r),i.previewCache.data=o,s},set:function(e,a,r,n){var s,o=i.previewCache.data;if(e&&e.length&&(t.isArray(e)||(e=e.split(i.initialPreviewDelimiter)),e.filter(function(e){return null!==e}).length)){if(void 0===o.content&&(o.content=[]),void 0===o.config&&(o.config=[]),void 0===o.tags&&(o.tags=[]),n){for(s=0;s'+e+"":"
        • "+e+"
        • ";return 0===a.find("ul").length?this._addError("
            "+n+"
          "):a.find("ul").append(n),a.fadeIn(800),this._raise(r,[t,e]),this._setValidationError("file-input-new"),!0},_showError:function(e,t,i){var a=this.$errorContainer,r=i||"fileerror";return(t=t||{}).reader=this.reader,this._addError(e),a.fadeIn(800),this._raise(r,[t,e]),this.isAjaxUpload||this._clearFileInput(),this._setValidationError("file-input-new"),this.$btnUpload.attr("disabled",!0),!0},_noFilesError:function(e){var t=this.minFileCount>1?this.filePlural:this.fileSingle,i=this.msgFilesTooLess.replace("{n}",this.minFileCount).replace("{files}",t),a=this.$errorContainer;this._addError(i),this.isError=!0,this._updateFileDetails(0),a.fadeIn(800),this._raise("fileerror",[e,i]),this._clearFileInput(),this._setValidationError()},_parseError:function(t,i,a,r){var n,s=e.trim(a+""),o=void 0!==i.responseJSON&&void 0!==i.responseJSON.error?i.responseJSON.error:i.responseText;return this.cancelling&&this.msgUploadAborted&&(s=this.msgUploadAborted),this.showAjaxErrorDetails&&o&&(n=(o=e.trim(o.replace(/\n\s*\n/g,"\n"))).length?"
          "+o+"
          ":"",s+=s?n:o),s||(s=this.msgAjaxError.replace("{operation}",t)),this.cancelling=!1,r?""+r+": "+s:s},_parseFileType:function(e,i){var a,r,n,s=this.allowedPreviewTypes||[];if("application/text-plain"===e)return"text";for(n=0;n-1&&(i=t.split(".").pop(),a.previewFileIconSettings&&(r=a.previewFileIconSettings[i]||a.previewFileIconSettings[i.toLowerCase()]||null),a.previewFileExtSettings&&e.each(a.previewFileExtSettings,function(e,t){a.previewFileIconSettings[e]&&t(i)&&(r=a.previewFileIconSettings[e])})),r},_parseFilePreviewIcon:function(e,t){var i=this._getPreviewIcon(t)||this.previewFileIcon,a=e;return a.indexOf("{previewFileIcon}")>-1&&(a=a.setTokens({previewFileIconClass:this.previewFileIconClass,previewFileIcon:i})),a},_raise:function(t,i){var a=e.Event(t);if(void 0!==i?this.$element.trigger(a,i):this.$element.trigger(a),a.isDefaultPrevented()||!1===a.result)return!1;switch(t){case"filebatchuploadcomplete":case"filebatchuploadsuccess":case"fileuploaded":case"fileclear":case"filecleared":case"filereset":case"fileerror":case"filefoldererror":case"fileuploaderror":case"filebatchuploaderror":case"filedeleteerror":case"filecustomerror":case"filesuccessremove":break;default:this.ajaxAborted||(this.ajaxAborted=a.result)}return!0},_listenFullScreen:function(e){var t,i,a=this.$modal;a&&a.length&&(t=a&&a.find(".btn-fullscreen"),i=a&&a.find(".btn-borderless"),t.length&&i.length&&(t.removeClass("active").attr("aria-pressed","false"),i.removeClass("active").attr("aria-pressed","false"),e?t.addClass("active").attr("aria-pressed","true"):i.addClass("active").attr("aria-pressed","true"),a.hasClass("file-zoom-fullscreen")?this._maximizeZoomDialog():e?this._maximizeZoomDialog():i.removeClass("active").attr("aria-pressed","false")))},_listen:function(){var i=this,a=i.$element,r=i.$form,n=i.$container;i._handler(a,"click",function(e){a.hasClass("file-no-browse")&&(a.data("zoneClicked")?a.data("zoneClicked",!1):e.preventDefault())}),i._handler(a,"change",e.proxy(i._change,i)),i.showBrowse&&i._handler(i.$btnFile,"click",e.proxy(i._browse,i)),i._handler(n.find(".fileinput-remove:not([disabled])"),"click",e.proxy(i.clear,i)),i._handler(n.find(".fileinput-cancel"),"click",e.proxy(i.cancel,i)),i._initDragDrop(),i._handler(r,"reset",e.proxy(i.clear,i)),i.isAjaxUpload||i._handler(r,"submit",e.proxy(i._submitForm,i)),i._handler(i.$container.find(".fileinput-upload"),"click",e.proxy(i._uploadClick,i)),i._handler(e(window),"resize",function(){i._listenFullScreen(screen.width===window.innerWidth&&screen.height===window.innerHeight)}),i._handler(e(document),"webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange",function(){i._listenFullScreen(t.checkFullScreen())}),i._autoFitContent(),i._initClickable(),i._refreshPreview()},_autoFitContent:function(){var t,i=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,a=this,r=i<400?a.previewSettingsSmall||a.defaults.previewSettingsSmall:a.previewSettings||a.defaults.previewSettings;e.each(r,function(e,i){t=".file-preview-frame .file-preview-"+e,a.$preview.find(t+".kv-preview-data,"+t+" .kv-preview-data").css(i)})},_scanDroppedItems:function(e,t,i){i=i||"";var a,r,n,s=this,o=function(e){s._log("Error scanning dropped files!"),s._log(e)};e.isFile?e.file(function(e){t.push(e)},o):e.isDirectory&&(r=e.createReader(),(n=function(){r.readEntries(function(r){if(r&&r.length>0){for(a=0;a-1;if(this._zoneDragDropInit(i),this.isDisabled||!a)return i.originalEvent.dataTransfer.effectAllowed="none",void(i.originalEvent.dataTransfer.dropEffect="none");t.addCss(this.$dropZone,"file-highlighted")},_zoneDragLeave:function(e){this._zoneDragDropInit(e),this.isDisabled||this.$dropZone.removeClass("file-highlighted")},_zoneDrop:function(e){var i,a=this,r=a.$element,n=e.originalEvent.dataTransfer,s=n.files,o=n.items,l=t.getDragDropFolders(o),d=function(){a.isAjaxUpload?a._change(e,s):(a.changeTriggered=!0,r.get(0).files=s,setTimeout(function(){a.changeTriggered=!1,r.trigger("change"+a.namespace)},10)),a.$dropZone.removeClass("file-highlighted")};if(e.preventDefault(),!a.isDisabled&&!t.isEmpty(s))if(l>0){if(!a.isAjaxUpload)return void a._showFolderError(l);for(s=[],i=0;i