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

{{ 'profile.show.username'|trans }}: {{ user.username }}

+

{{ 'profile.show.email'|trans }}: {{ user.email }}

+
diff --git a/app/Resources/FOSUserBundle/views/Resetting/request_content.html.twig b/app/Resources/FOSUserBundle/views/Resetting/request_content.html.twig new file mode 100644 index 000000000..d652e1d80 --- /dev/null +++ b/app/Resources/FOSUserBundle/views/Resetting/request_content.html.twig @@ -0,0 +1,20 @@ +{% trans_default_domain 'FOSUserBundle' %} + + + +
+
+
+ {% if invalid_username is defined %} +

{{ 'resetting.request.invalid_username'|trans({'%username%': invalid_username}) }}

+ {% endif %} + + +
+
+

+
+
+
\ No newline at end of file diff --git a/app/Resources/FOSUserBundle/views/Resetting/reset_content.html.twig b/app/Resources/FOSUserBundle/views/Resetting/reset_content.html.twig new file mode 100644 index 000000000..0d79f285f --- /dev/null +++ b/app/Resources/FOSUserBundle/views/Resetting/reset_content.html.twig @@ -0,0 +1,14 @@ +{% trans_default_domain 'FOSUserBundle' %} + + + +
+
+ {{ form_widget(form) }} +
+

+
+
+
\ No newline at end of file diff --git a/app/Resources/FOSUserBundle/views/layout.html.twig b/app/Resources/FOSUserBundle/views/layout.html.twig new file mode 100644 index 000000000..ae9f517b5 --- /dev/null +++ b/app/Resources/FOSUserBundle/views/layout.html.twig @@ -0,0 +1,29 @@ + + + + + + +
+ {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} + {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} | + + {{ 'layout.logout'|trans({}, 'FOSUserBundle') }} + + {% endif %} +
+ + {% for type, messages in app.session.flashbag.all() %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endfor %} + +
+ {% block fos_user_content %} + {% endblock fos_user_content %} +
+ + diff --git a/app/Resources/TwigBundle/views/Exception/error404.html.twig b/app/Resources/TwigBundle/views/Exception/error404.html.twig new file mode 100644 index 000000000..ce1e3918e --- /dev/null +++ b/app/Resources/TwigBundle/views/Exception/error404.html.twig @@ -0,0 +1,19 @@ + + + + + An Error Occurred: {{ status_text }} + + +

Oops! An Error Occurred

+

The server returned a "{{ status_code }} {{ status_text }}".

+ +
+ Something is broken. Please e-mail us at contact@myddleware.com and let us know + what you were doing when this error occurred. We will fix it as soon + as possible.
+ You can also log the issue on our Github : Myddleware Github project

+ Sorry for any inconvenience caused. +
+ + \ No newline at end of file diff --git a/app/Resources/TwigBundle/views/Exception/error500.html.twig b/app/Resources/TwigBundle/views/Exception/error500.html.twig new file mode 100644 index 000000000..ce1e3918e --- /dev/null +++ b/app/Resources/TwigBundle/views/Exception/error500.html.twig @@ -0,0 +1,19 @@ + + + + + An Error Occurred: {{ status_text }} + + +

Oops! An Error Occurred

+

The server returned a "{{ status_code }} {{ status_text }}".

+ +
+ Something is broken. Please e-mail us at contact@myddleware.com and let us know + what you were doing when this error occurred. We will fix it as soon + as possible.
+ You can also log the issue on our Github : Myddleware Github project

+ Sorry for any inconvenience caused. +
+ + \ No newline at end of file diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig new file mode 100644 index 000000000..90cfe1bd7 --- /dev/null +++ b/app/Resources/views/base.html.twig @@ -0,0 +1,200 @@ + {#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ #} + + + + + + {% block title %}Myddleware{% endblock %} + + {% block stylesheets %} + {% stylesheets + 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css' + '@RegleBundle/Resources/public/css/layout.css' + '@RegleBundle/Resources/public/css/menu.css' + '@RegleBundle/Resources/public/css/regle.css' + 'http://code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css' + 'http://trentrichardson.com/examples/timepicker/jquery-ui-timepicker-addon.css' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_fancybox/jquery.fancybox.css' + 'http://cdn.jsdelivr.net/qtip2/2.2.0/jquery.qtip.min.css' + filter='lessphp,cssrewrite' + output='css/compiled/main.css' + %} + + + {% endstylesheets %} + {% endblock %} + + {% block css %}{% endblock css %} + + + + + + + + + +
+ +
+
+ {# Notification #} + {% include "RegleBundle:Layout:notification.html.twig" %} +
+
{% block titlesm %}{% endblock titlesm %}
+
+ {# Content #} + {% block body %} + {% block arianne %}Myddleware{% endblock %} + {% endblock %} +
+
+ {# Footer #} + {% include 'footer.html.twig' %} +
+ + {# Javascript #} + {% javascripts + '%kernel.root_dir%/../vendor/jquery/jquery/jquery-2.1.3.min.js' + '%kernel.root_dir%/../vendor/jquery/jquery_ui/jquery-ui.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_onoff/jquery.onoff.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_fancybox/jquery.fancybox.pack.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_scrollbox/jquery.scrollbox.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery-ui-timepicker-addon/jquery-ui-timepicker-addon.js' + 'http://cdn.jsdelivr.net/qtip2/2.2.0/jquery.qtip.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_myddleware/function.js' + '@RegleBundle/Resources/public/js/regle.js' + %} + + {% endjavascripts %} + + + + + + + {% block js %}{% endblock js %} + + diff --git a/app/Resources/views/baseone.html.twig b/app/Resources/views/baseone.html.twig new file mode 100644 index 000000000..eac822a51 --- /dev/null +++ b/app/Resources/views/baseone.html.twig @@ -0,0 +1,81 @@ + {#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ #} + + + + + + {% block title %}Myddleware{% endblock %} + {% block stylesheets %} + {% stylesheets + '@RegleBundle/Resources/public/css/regle.css' + 'http://code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css' + 'http://trentrichardson.com/examples/timepicker/jquery-ui-timepicker-addon.css' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_fancybox/jquery.fancybox.css' + 'http://cdn.jsdelivr.net/qtip2/2.2.0/jquery.qtip.min.css' + filter='lessphp,cssrewrite' + output='css/compiled/main.css' + %} + + + {% endstylesheets %} + {% endblock %} + + + {% block css %}{% endblock css %} + + + + + + + {# Content #} + {% block body %} + {% block arianne %}Myddleware{% endblock %} + {% endblock %} + + {# Javascript #} + {% javascripts + '%kernel.root_dir%/../vendor/jquery/jquery/jquery-2.1.3.min.js' + '%kernel.root_dir%/../vendor/jquery/jquery_ui/jquery-ui.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_onoff/jquery.onoff.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_fancybox/jquery.fancybox.pack.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery-ui-timepicker-addon/jquery-ui-timepicker-addon.js' + 'http://cdn.jsdelivr.net/qtip2/2.2.0/jquery.qtip.min.js' + '%kernel.root_dir%/../src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_myddleware/function.js' + '@LoginBundle/Resources/public/js/login.js' + '@RegleBundle/Resources/public/js/regle.js' + %} + + {% endjavascripts %} + + + + + {% block js %}{% endblock js %} + + diff --git a/app/Resources/views/css.html.twig b/app/Resources/views/css.html.twig new file mode 100644 index 000000000..0100dde51 --- /dev/null +++ b/app/Resources/views/css.html.twig @@ -0,0 +1,166 @@ + {#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ #} + +body { + background: url({{ asset('bundles/regle/images/') }}template/fd.jpg) repeat-y; + background-color: #9DC0E1; +} + +#logo { + background: url({{ asset('bundles/regle/images/') }}template/logo.png) no-repeat; + background-size: 158px; + height: 113px; + width: 160px; + display: inline-block; + margin-top: 50px; +} + + +menu .caret, #myd_top .caret { + background: url({{ asset('bundles/regle/images/') }}template/arrow-menu.png) no-repeat; + height: 13px; + width: 14px; + border: none; +} + +#cloud { + background: url({{ asset('bundles/regle/images/') }}template/cloud.png) no-repeat; + height: 129px; + width: 397px; + display: inline-block; + border: none; +} + +#fd-title-small { + background: url({{ asset('bundles/regle/images/') }}template/fd-title-small.png) no-repeat; + height: 71px; + width: 179px; + display: inline-block; + border: none; + margin: 20px 20px 20px -13px; + padding-top: 15px; + color: white; + text-align: center; + line-height: 1em; +} + +#fd-title-large { + background: url({{ asset('bundles/regle/images/') }}template/fd-title-large.png) no-repeat; + height: 71px; + width: 600px; + padding: 15px 0 0 16px; + display: inline-block; + border: none; + margin: 20px 20px 20px -13px; + color: white; + text-align: left; + line-height: 1em; +} + +#fd-title-large p { + margin: 0 0 2px !important; +} + +#fd-title { + background: url({{ asset('bundles/regle/images/') }}template/fd-title.png) no-repeat; + height: 71px; + width: 293px; + display: inline-block; + border: none; + margin: 20px 20px 20px -13px; + padding-top: 15px; + color: white; + text-align: center; + + font-size: 0.9em; + line-height: 0.6em; + padding-left: 8px; + text-align: left; +} + +/* boutons */ + +.btn-myd, .btn-mydinv, .btn-mydinv:hover, .btn-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn.png) no-repeat; + height: 31px; + min-width: 196px; + border: none; + display:inline-block; + line-height: 2em; + text-align: center; +} + +.btn-small-myd, .btn-small-mydinv, .btn-small-mydinv:hover, .btn-small-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn-small.png) no-repeat; + height: 31px; + width: 50px; + border: none; + display:inline-block; +} + +.btn-middle-myd, .btn-myddle-mydinv, .btn-middle-mydinv:hover, .btn-middle-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn-middle.png) no-repeat; + height: 31px; + width: 100px; + border: none; + display:inline-block; +} + +.btn-large-myd, .btn-large-mydinv, .btn-large-mydinv:hover, .btn-large-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn-large.png) no-repeat; + height: 31px; + width: 340px; + border: none; + display:inline-block; +} + + +.btn-myd, .btn-small-myd, .btn-middle-myd, .btn-large-myd { + background-position: left top; +} + +.btn-myd:hover, .btn-small-myd:hover, .btn-middle-myd:hover, .btn-large-myd:hover { + background-position: left bottom; + text-decoration:none; + cursor:pointer; +} + +.btn-mydinv, .btn-small-mydinv, .btn-middle-mydinv, .btn-large-mydinv { + background-position: left bottom; +} + +.btn-mydinv:hover, .btn-small-mydinv:hover, .btn-middle-mydinv:hover, .btn-large-mydinv:hover { + background-position: left top; + text-decoration:none; + cursor:pointer; +} + diff --git a/app/Resources/views/footer.html.twig b/app/Resources/views/footer.html.twig new file mode 100644 index 000000000..e3021404c --- /dev/null +++ b/app/Resources/views/footer.html.twig @@ -0,0 +1,26 @@ + {#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ #} + +

© Myddleware 2013-{{ "now"|date("Y") }}

+

v{{myd_version}}

\ No newline at end of file diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php new file mode 100644 index 000000000..28b0dcdbf --- /dev/null +++ b/app/SymfonyRequirements.php @@ -0,0 +1,764 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Users of PHP 5.2 should be able to run the requirements checks. + * This is why the file and all classes must be compatible with PHP 5.2+ + * (e.g. not using namespaces and closures). + * + * ************** CAUTION ************** + * + * DO NOT EDIT THIS FILE as it will be overridden by Composer as part of + * the installation/update process. The original file resides in the + * SensioDistributionBundle. + * + * ************** CAUTION ************** + */ + +/** + * Represents a single PHP requirement, e.g. an installed extension. + * It can be a mandatory requirement or an optional recommendation. + * There is a special subclass, named PhpIniRequirement, to check a php.ini configuration. + * + * @author Tobias Schultze + */ +class Requirement +{ + private $fulfilled; + private $testMessage; + private $helpText; + private $helpHtml; + private $optional; + + /** + * Constructor that initializes the requirement. + * + * @param bool $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) + { + $this->fulfilled = (bool) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (bool) $optional; + } + + /** + * Returns whether the requirement is fulfilled. + * + * @return bool true if fulfilled, otherwise false + */ + public function isFulfilled() + { + return $this->fulfilled; + } + + /** + * Returns the message for testing the requirement. + * + * @return string The test message + */ + public function getTestMessage() + { + return $this->testMessage; + } + + /** + * Returns the help text for resolving the problem. + * + * @return string The help text + */ + public function getHelpText() + { + return $this->helpText; + } + + /** + * Returns the help text formatted in HTML. + * + * @return string The HTML help + */ + public function getHelpHtml() + { + return $this->helpHtml; + } + + /** + * Returns whether this is only an optional recommendation and not a mandatory requirement. + * + * @return bool true if optional, false if mandatory + */ + public function isOptional() + { + return $this->optional; + } +} + +/** + * Represents a PHP requirement in form of a php.ini configuration. + * + * @author Tobias Schultze + */ +class PhpIniRequirement extends Requirement +{ + /** + * Constructor that initializes the requirement. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + */ + public function __construct($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null, $optional = false) + { + $cfgValue = ini_get($cfgName); + + if (is_callable($evaluation)) { + if (null === $testMessage || null === $helpHtml) { + throw new InvalidArgumentException('You must provide the parameters testMessage and helpHtml for a callback evaluation.'); + } + + $fulfilled = call_user_func($evaluation, $cfgValue); + } else { + if (null === $testMessage) { + $testMessage = sprintf('%s %s be %s in php.ini', + $cfgName, + $optional ? 'should' : 'must', + $evaluation ? 'enabled' : 'disabled' + ); + } + + if (null === $helpHtml) { + $helpHtml = sprintf('Set %s to %s in php.ini*.', + $cfgName, + $evaluation ? 'on' : 'off' + ); + } + + $fulfilled = $evaluation == $cfgValue; + } + + parent::__construct($fulfilled || ($approveCfgAbsence && false === $cfgValue), $testMessage, $helpHtml, $helpText, $optional); + } +} + +/** + * A RequirementCollection represents a set of Requirement instances. + * + * @author Tobias Schultze + */ +class RequirementCollection implements IteratorAggregate +{ + private $requirements = array(); + + /** + * Gets the current RequirementCollection as an Iterator. + * + * @return Traversable A Traversable interface + */ + public function getIterator() + { + return new ArrayIterator($this->requirements); + } + + /** + * Adds a Requirement. + * + * @param Requirement $requirement A Requirement instance + */ + public function add(Requirement $requirement) + { + $this->requirements[] = $requirement; + } + + /** + * Adds a mandatory requirement. + * + * @param bool $fulfilled Whether the requirement is fulfilled + * @param string $testMessage The message for testing the requirement + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation. + * + * @param bool $fulfilled Whether the recommendation is fulfilled + * @param string $testMessage The message for testing the recommendation + * @param string $helpHtml The help text formatted in HTML for resolving the problem + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) + { + $this->add(new Requirement($fulfilled, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a mandatory requirement in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, false)); + } + + /** + * Adds an optional recommendation in form of a php.ini configuration. + * + * @param string $cfgName The configuration name used for ini_get() + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, + * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. + * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. + * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + */ + public function addPhpIniRecommendation($cfgName, $evaluation, $approveCfgAbsence = false, $testMessage = null, $helpHtml = null, $helpText = null) + { + $this->add(new PhpIniRequirement($cfgName, $evaluation, $approveCfgAbsence, $testMessage, $helpHtml, $helpText, true)); + } + + /** + * Adds a requirement collection to the current set of requirements. + * + * @param RequirementCollection $collection A RequirementCollection instance + */ + public function addCollection(RequirementCollection $collection) + { + $this->requirements = array_merge($this->requirements, $collection->all()); + } + + /** + * Returns both requirements and recommendations. + * + * @return array Array of Requirement instances + */ + public function all() + { + return $this->requirements; + } + + /** + * Returns all mandatory requirements. + * + * @return array Array of Requirement instances + */ + public function getRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the mandatory requirements that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRequirements() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && !$req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns all optional recommendations. + * + * @return array Array of Requirement instances + */ + public function getRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if ($req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns the recommendations that were not met. + * + * @return array Array of Requirement instances + */ + public function getFailedRecommendations() + { + $array = array(); + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req->isOptional()) { + $array[] = $req; + } + } + + return $array; + } + + /** + * Returns whether a php.ini configuration is not correct. + * + * @return bool php.ini configuration problem? + */ + public function hasPhpIniConfigIssue() + { + foreach ($this->requirements as $req) { + if (!$req->isFulfilled() && $req instanceof PhpIniRequirement) { + return true; + } + } + + return false; + } + + /** + * Returns the PHP configuration file (php.ini) path. + * + * @return string|false php.ini file path + */ + public function getPhpIniConfigPath() + { + return get_cfg_var('cfg_file_path'); + } +} + +/** + * This class specifies all requirements and optional recommendations that + * are necessary to run the Symfony Standard Edition. + * + * @author Tobias Schultze + * @author Fabien Potencier + */ +class SymfonyRequirements extends RequirementCollection +{ + const REQUIRED_PHP_VERSION = '5.3.3'; + + /** + * Constructor that initializes the requirements. + */ + public function __construct() + { + /* mandatory requirements follow */ + + $installedPhpVersion = phpversion(); + + $this->addRequirement( + version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>='), + sprintf('PHP version must be at least %s (%s installed)', self::REQUIRED_PHP_VERSION, $installedPhpVersion), + sprintf('You are running PHP version "%s", but Symfony needs at least PHP "%s" to run. + Before using Symfony, upgrade your PHP installation, preferably to the latest version.', + $installedPhpVersion, self::REQUIRED_PHP_VERSION), + sprintf('Install PHP %s or newer (installed version is %s)', self::REQUIRED_PHP_VERSION, $installedPhpVersion) + ); + + $this->addRequirement( + version_compare($installedPhpVersion, '5.3.16', '!='), + 'PHP version must not be 5.3.16 as Symfony won\'t work properly with it', + 'Install PHP 5.3.17 or newer (or downgrade to an earlier PHP version)' + ); + + $this->addRequirement( + is_dir(__DIR__.'/../vendor/composer'), + 'Vendor libraries must be installed', + 'Vendor libraries are missing. Install composer following instructions from http://getcomposer.org/. '. + 'Then run "php composer.phar install" to install them.' + ); + + $cacheDir = is_dir(__DIR__.'/../var/cache') ? __DIR__.'/../var/cache' : __DIR__.'/cache'; + + $this->addRequirement( + is_writable($cacheDir), + 'app/cache/ or var/cache/ directory must be writable', + 'Change the permissions of either "app/cache/" or "var/cache/" directory so that the web server can write into it.' + ); + + $logsDir = is_dir(__DIR__.'/../var/logs') ? __DIR__.'/../var/logs' : __DIR__.'/logs'; + + $this->addRequirement( + is_writable($logsDir), + 'app/logs/ or var/logs/ directory must be writable', + 'Change the permissions of either "app/logs/" or "var/logs/" directory so that the web server can write into it.' + ); + + $this->addPhpIniRequirement( + 'date.timezone', true, false, + 'date.timezone setting must be set', + 'Set the "date.timezone" setting in php.ini* (like Europe/Paris).' + ); + + if (version_compare($installedPhpVersion, self::REQUIRED_PHP_VERSION, '>=')) { + $timezones = array(); + foreach (DateTimeZone::listAbbreviations() as $abbreviations) { + foreach ($abbreviations as $abbreviation) { + $timezones[$abbreviation['timezone_id']] = true; + } + } + + $this->addRequirement( + isset($timezones[@date_default_timezone_get()]), + sprintf('Configured default timezone "%s" must be supported by your installation of PHP', @date_default_timezone_get()), + 'Your default timezone is not supported by PHP. Check for typos in your php.ini file and have a look at the list of deprecated timezones at http://php.net/manual/en/timezones.others.php.' + ); + } + + $this->addRequirement( + function_exists('iconv'), + 'iconv() must be available', + 'Install and enable the iconv extension.' + ); + + $this->addRequirement( + function_exists('json_encode'), + 'json_encode() must be available', + 'Install and enable the JSON extension.' + ); + + $this->addRequirement( + function_exists('session_start'), + 'session_start() must be available', + 'Install and enable the session extension.' + ); + + $this->addRequirement( + function_exists('ctype_alpha'), + 'ctype_alpha() must be available', + 'Install and enable the ctype extension.' + ); + + $this->addRequirement( + function_exists('token_get_all'), + 'token_get_all() must be available', + 'Install and enable the Tokenizer extension.' + ); + + $this->addRequirement( + function_exists('simplexml_import_dom'), + 'simplexml_import_dom() must be available', + 'Install and enable the SimpleXML extension.' + ); + + if (function_exists('apc_store') && ini_get('apc.enabled')) { + if (version_compare($installedPhpVersion, '5.4.0', '>=')) { + $this->addRequirement( + version_compare(phpversion('apc'), '3.1.13', '>='), + 'APC version must be at least 3.1.13 when using PHP 5.4', + 'Upgrade your APC extension (3.1.13+).' + ); + } else { + $this->addRequirement( + version_compare(phpversion('apc'), '3.0.17', '>='), + 'APC version must be at least 3.0.17', + 'Upgrade your APC extension (3.0.17+).' + ); + } + } + + $this->addPhpIniRequirement('detect_unicode', false); + + if (extension_loaded('suhosin')) { + $this->addPhpIniRequirement( + 'suhosin.executor.include.whitelist', + create_function('$cfgValue', 'return false !== stripos($cfgValue, "phar");'), + false, + 'suhosin.executor.include.whitelist must be configured correctly in php.ini', + 'Add "phar" to suhosin.executor.include.whitelist in php.ini*.' + ); + } + + if (extension_loaded('xdebug')) { + $this->addPhpIniRequirement( + 'xdebug.show_exception_trace', false, true + ); + + $this->addPhpIniRequirement( + 'xdebug.scream', false, true + ); + + $this->addPhpIniRecommendation( + 'xdebug.max_nesting_level', + create_function('$cfgValue', 'return $cfgValue > 100;'), + true, + 'xdebug.max_nesting_level should be above 100 in php.ini', + 'Set "xdebug.max_nesting_level" to e.g. "250" in php.ini* to stop Xdebug\'s infinite recursion protection erroneously throwing a fatal error in your project.' + ); + } + + $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; + + $this->addRequirement( + null !== $pcreVersion, + 'PCRE extension must be available', + 'Install the PCRE extension (version 8.0+).' + ); + + if (extension_loaded('mbstring')) { + $this->addPhpIniRequirement( + 'mbstring.func_overload', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'string functions should not be overloaded', + 'Set "mbstring.func_overload" to 0 in php.ini* to disable function overloading by the mbstring extension.' + ); + } + + /* optional recommendations follow */ + + if (file_exists(__DIR__.'/../vendor/composer')) { + require_once __DIR__.'/../vendor/autoload.php'; + + try { + $r = new ReflectionClass('Sensio\Bundle\DistributionBundle\SensioDistributionBundle'); + + $contents = file_get_contents(dirname($r->getFileName()).'/Resources/skeleton/app/SymfonyRequirements.php'); + } catch (ReflectionException $e) { + $contents = ''; + } + $this->addRecommendation( + file_get_contents(__FILE__) === $contents, + 'Requirements file should be up-to-date', + 'Your requirements file is outdated. Run composer install and re-check your configuration.' + ); + } + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.4', '>='), + 'You should use at least PHP 5.3.4 due to PHP bug #52083 in earlier versions', + 'Your project might malfunction randomly due to PHP bug #52083 ("Notice: Trying to get property of non-object"). Install PHP 5.3.4 or newer.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.3.8', '>='), + 'When using annotations you should have at least PHP 5.3.8 due to PHP bug #55156', + 'Install PHP 5.3.8 or newer if your project uses annotations.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.4.0', '!='), + 'You should not use PHP 5.4.0 due to the PHP bug #61453', + 'Your project might not work properly due to the PHP bug #61453 ("Cannot dump definitions which have method calls"). Install PHP 5.4.1 or newer.' + ); + + $this->addRecommendation( + version_compare($installedPhpVersion, '5.4.11', '>='), + 'When using the logout handler from the Symfony Security Component, you should have at least PHP 5.4.11 due to PHP bug #63379 (as a workaround, you can also set invalidate_session to false in the security logout handler configuration)', + 'Install PHP 5.4.11 or newer if your project uses the logout handler from the Symfony Security Component.' + ); + + $this->addRecommendation( + (version_compare($installedPhpVersion, '5.3.18', '>=') && version_compare($installedPhpVersion, '5.4.0', '<')) + || + version_compare($installedPhpVersion, '5.4.8', '>='), + 'You should use PHP 5.3.18+ or PHP 5.4.8+ to always get nice error messages for fatal errors in the development environment due to PHP bug #61767/#60909', + 'Install PHP 5.3.18+ or PHP 5.4.8+ if you want nice error messages for all fatal errors in the development environment.' + ); + + if (null !== $pcreVersion) { + $this->addRecommendation( + $pcreVersion >= 8.0, + sprintf('PCRE extension should be at least version 8.0 (%s installed)', $pcreVersion), + 'PCRE 8.0+ is preconfigured in PHP since 5.3.2 but you are using an outdated version of it. Symfony probably works anyway but it is recommended to upgrade your PCRE extension.' + ); + } + + $this->addRecommendation( + class_exists('DomDocument'), + 'PHP-DOM and PHP-XML modules should be installed', + 'Install and enable the PHP-DOM and the PHP-XML modules.' + ); + + $this->addRecommendation( + function_exists('mb_strlen'), + 'mb_strlen() should be available', + 'Install and enable the mbstring extension.' + ); + + $this->addRecommendation( + function_exists('iconv'), + 'iconv() should be available', + 'Install and enable the iconv extension.' + ); + + $this->addRecommendation( + function_exists('utf8_decode'), + 'utf8_decode() should be available', + 'Install and enable the XML extension.' + ); + + $this->addRecommendation( + function_exists('filter_var'), + 'filter_var() should be available', + 'Install and enable the filter extension.' + ); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->addRecommendation( + function_exists('posix_isatty'), + 'posix_isatty() should be available', + 'Install and enable the php_posix extension (used to colorize the CLI output).' + ); + } + + $this->addRecommendation( + extension_loaded('intl'), + 'intl extension should be available', + 'Install and enable the intl extension (used for validators).' + ); + + if (extension_loaded('intl')) { + // in some WAMP server installations, new Collator() returns null + $this->addRecommendation( + null !== new Collator('fr_FR'), + 'intl extension should be correctly configured', + 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' + ); + + // check for compatible ICU versions (only done when you have the intl extension) + if (defined('INTL_ICU_VERSION')) { + $version = INTL_ICU_VERSION; + } else { + $reflector = new ReflectionExtension('intl'); + + ob_start(); + $reflector->info(); + $output = strip_tags(ob_get_clean()); + + preg_match('/^ICU version +(?:=> )?(.*)$/m', $output, $matches); + $version = $matches[1]; + } + + $this->addRecommendation( + version_compare($version, '4.0', '>='), + 'intl ICU version should be at least 4+', + 'Upgrade your intl extension with a newer ICU version (4+).' + ); + + $this->addPhpIniRecommendation( + 'intl.error_level', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'intl.error_level should be 0 in php.ini', + 'Set "intl.error_level" to "0" in php.ini* to inhibit the messages when an error occurs in ICU functions.' + ); + } + + $accelerator = + (extension_loaded('eaccelerator') && ini_get('eaccelerator.enable')) + || + (extension_loaded('apc') && ini_get('apc.enabled')) + || + (extension_loaded('Zend Optimizer+') && ini_get('zend_optimizerplus.enable')) + || + (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) + || + (extension_loaded('xcache') && ini_get('xcache.cacher')) + || + (extension_loaded('wincache') && ini_get('wincache.ocenabled')) + ; + + $this->addRecommendation( + $accelerator, + 'a PHP accelerator should be installed', + 'Install and/or enable a PHP accelerator (highly recommended).' + ); + + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $this->addRecommendation( + $this->getRealpathCacheSize() > 1000, + 'realpath_cache_size should be above 1024 in php.ini', + 'Set "realpath_cache_size" to e.g. "1024" in php.ini* to improve performance on windows.' + ); + } + + $this->addPhpIniRecommendation('short_open_tag', false); + + $this->addPhpIniRecommendation('magic_quotes_gpc', false, true); + + $this->addPhpIniRecommendation('register_globals', false, true); + + $this->addPhpIniRecommendation('session.auto_start', false); + + $this->addRecommendation( + class_exists('PDO'), + 'PDO should be installed', + 'Install PDO (mandatory for Doctrine).' + ); + + if (class_exists('PDO')) { + $drivers = PDO::getAvailableDrivers(); + $this->addRecommendation( + count($drivers) > 0, + sprintf('PDO should have some drivers installed (currently available: %s)', count($drivers) ? implode(', ', $drivers) : 'none'), + 'Install PDO drivers (mandatory for Doctrine).' + ); + } + } + + /** + * Loads realpath_cache_size from php.ini and converts it to int. + * + * (e.g. 16k is converted to 16384 int) + * + * @return int + */ + protected function getRealpathCacheSize() + { + $size = ini_get('realpath_cache_size'); + $size = trim($size); + $unit = strtolower(substr($size, -1, 1)); + switch ($unit) { + case 'g': + return $size * 1024 * 1024 * 1024; + case 'm': + return $size * 1024 * 1024; + case 'k': + return $size * 1024; + default: + return (int) $size; + } + } +} diff --git a/app/autoload.php b/app/autoload.php new file mode 100644 index 000000000..70526bb5e --- /dev/null +++ b/app/autoload.php @@ -0,0 +1,13 @@ +getPhpIniConfigPath(); + +echo_title('Symfony Requirements Checker'); + +echo '> PHP is using the following php.ini file:'.PHP_EOL; +if ($iniPath) { + echo_style('green', ' '.$iniPath); +} else { + echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!'); +} + +echo PHP_EOL.PHP_EOL; + +echo '> Checking Symfony requirements:'.PHP_EOL.' '; + +$messages = array(); +foreach ($symfonyRequirements->getRequirements() as $req) { + /** @var $req Requirement */ + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('red', 'E'); + $messages['error'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +$checkPassed = empty($messages['error']); + +foreach ($symfonyRequirements->getRecommendations() as $req) { + if ($helpText = get_error_message($req, $lineSize)) { + echo_style('yellow', 'W'); + $messages['warning'][] = $helpText; + } else { + echo_style('green', '.'); + } +} + +if ($checkPassed) { + echo_block('success', 'OK', 'Your system is ready to run Symfony projects'); +} else { + echo_block('error', 'ERROR', 'Your system is not ready to run Symfony projects'); + + echo_title('Fix the following mandatory requirements', 'red'); + + foreach ($messages['error'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +if (!empty($messages['warning'])) { + echo_title('Optional recommendations to improve your setup', 'yellow'); + + foreach ($messages['warning'] as $helpText) { + echo ' * '.$helpText.PHP_EOL; + } +} + +echo PHP_EOL; +echo_style('title', 'Note'); +echo ' The command console could use a different php.ini file'.PHP_EOL; +echo_style('title', '~~~~'); +echo ' than the one used with your web server. To be on the'.PHP_EOL; +echo ' safe side, please check the requirements from your web'.PHP_EOL; +echo ' server using the '; +echo_style('yellow', 'web/config.php'); +echo ' script.'.PHP_EOL; +echo PHP_EOL; + +exit($checkPassed ? 0 : 1); + +function get_error_message(Requirement $requirement, $lineSize) +{ + if ($requirement->isFulfilled()) { + return; + } + + $errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL; + $errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL; + + return $errorMessage; +} + +function echo_title($title, $style = null) +{ + $style = $style ?: 'title'; + + echo PHP_EOL; + echo_style($style, $title.PHP_EOL); + echo_style($style, str_repeat('~', strlen($title)).PHP_EOL); + echo PHP_EOL; +} + +function echo_style($style, $message) +{ + // ANSI color codes + $styles = array( + 'reset' => "\033[0m", + 'red' => "\033[31m", + 'green' => "\033[32m", + 'yellow' => "\033[33m", + 'error' => "\033[37;41m", + 'success' => "\033[37;42m", + 'title' => "\033[34m", + ); + $supports = has_color_support(); + + echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); +} + +function echo_block($style, $title, $message) +{ + $message = ' '.trim($message).' '; + $width = strlen($message); + + echo PHP_EOL.PHP_EOL; + + echo_style($style, str_repeat(' ', $width).PHP_EOL); + echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL); + echo_style($style, str_repeat(' ', $width).PHP_EOL); +} + +function has_color_support() +{ + static $support; + + if (null === $support) { + if (DIRECTORY_SEPARATOR == '\\') { + $support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI'); + } else { + $support = function_exists('posix_isatty') && @posix_isatty(STDOUT); + } + } + + return $support; +} diff --git a/app/config/config.yml b/app/config/config.yml new file mode 100644 index 000000000..1fa0a8624 --- /dev/null +++ b/app/config/config.yml @@ -0,0 +1,97 @@ +imports: + - { resource: parameters.yml } + - { resource: security.yml } + +framework: + #esi: ~ + translator: { fallback: %locale% } + secret: %secret% + router: + resource: "%kernel.root_dir%/config/routing.yml" + strict_requirements: ~ + form: ~ + csrf_protection: ~ + validation: { enable_annotations: true } + templating: + engines: ['twig'] + #assets_version: SomeVersionScheme + default_locale: "%locale%" + trusted_proxies: ~ + session: ~ + fragments: ~ + http_method_override: true + +# Twig Configuration +twig: + debug: %kernel.debug% + strict_variables: %kernel.debug% + globals: + myd_version: %myd_version% + +# Assetic Configuration +assetic: + debug: %kernel.debug% + use_controller: false + bundles: [ ] + #java: /usr/bin/java + filters: + lessphp: + file: %kernel.root_dir%/../vendor/leafo/lessphp/lessc.inc.php + apply_to: "\.less$" + cssrewrite: ~ + #closure: + # jar: %kernel.root_dir%/Resources/java/compiler.jar + #yui_css: + # jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar + + +# Doctrine Configuration +doctrine: + dbal: + driver: %database_driver% + host: %database_host% + port: %database_port% + dbname: %database_name% + user: %database_user% + password: %database_password% + charset: UTF8 + # if using pdo_sqlite as your database driver, add the path in parameters.yml + # e.g. database_path: %kernel.root_dir%/data/data.db3 + # path: %database_path% + + orm: + auto_generate_proxy_classes: %kernel.debug% + auto_mapping: true + +# Swiftmailer Configuration +swiftmailer: + transport: %mailer_transport% + host: %mailer_host% + username: %mailer_user% + password: %mailer_password% + port: %mailer_port% + auth_mode: %mailer_auth_mode% + spool: { type: memory } + +# FOSUserBundle configuration +fos_user: + db_driver: orm + firewall_name: main + user_class: Myddleware\LoginBundle\Entity\User + from_email: + address: contact@myddleware.com + sender_name: Myddleware contact + +# Stof\DoctrineExtensionBundle configuration +stof_doctrine_extensions: + orm: + default: + sluggable: true + +# Activation de l'extension Twig intl +services: + twig.extension.intl: + class: Twig_Extensions_Extension_Intl + tags: + - { name: twig.extension } + diff --git a/app/config/config_background.yml b/app/config/config_background.yml new file mode 100644 index 000000000..e3393e186 --- /dev/null +++ b/app/config/config_background.yml @@ -0,0 +1,16 @@ +imports: + - { resource: config.yml } + +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + nested: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: error + +parameters: + notification_emailaddress: \ No newline at end of file diff --git a/app/config/config_dev.yml b/app/config/config_dev.yml new file mode 100644 index 000000000..ccc9ac2d0 --- /dev/null +++ b/app/config/config_dev.yml @@ -0,0 +1,37 @@ +imports: + - { resource: config.yml } + +framework: + router: + resource: "%kernel.root_dir%/config/routing_dev.yml" + strict_requirements: true + profiler: { only_exceptions: false } + +web_profiler: + toolbar: true + intercept_redirects: false + +monolog: + handlers: + main: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: error + firephp: + type: firephp + level: error + chromephp: + type: chromephp + level: error + +assetic: + use_controller: true + +services: + twig.extension.debug: + class: Twig_Extension_Debug + tags: + - { name: 'twig.extension' } + +#swiftmailer: +# delivery_address: me@example.com diff --git a/app/config/config_prod.yml b/app/config/config_prod.yml new file mode 100644 index 000000000..7dc673360 --- /dev/null +++ b/app/config/config_prod.yml @@ -0,0 +1,23 @@ +imports: + - { resource: config.yml } + +#framework: +# validation: +# cache: apc + +#doctrine: +# orm: +# metadata_cache_driver: apc +# result_cache_driver: apc +# query_cache_driver: apc + +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + nested: + type: stream + path: %kernel.logs_dir%/%kernel.environment%.log + level: error diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist new file mode 100644 index 000000000..87732464e --- /dev/null +++ b/app/config/parameters.yml.dist @@ -0,0 +1,28 @@ +parameters: + database_driver: pdo_mysql + database_host: + database_port: + database_name: + database_user: + database_password: + mailer_transport: mail + mailer_host: + mailer_port: + mailer_auth_mode: + mailer_user: + mailer_password: + mailer_email: + mailer_name: + secret: + myddleware_support: 0 + param: + mode: front_office + extension_allowed: + - xml + - wsdl + myd_version: 2.0.2 + php: + executable: php + locale: en + pager: 20 + block_install: 0 diff --git a/app/config/routing.yml b/app/config/routing.yml new file mode 100644 index 000000000..545390a81 --- /dev/null +++ b/app/config/routing.yml @@ -0,0 +1,26 @@ +regle: + resource: "@RegleBundle/Resources/config/routing.yml" + prefix: /rule + +login: + resource: "@LoginBundle/Resources/config/routing.yml" + prefix: / + +fos_user_security: + resource: "@FOSUserBundle/Resources/config/routing/security.xml" + +fos_user_profile: + resource: "@FOSUserBundle/Resources/config/routing/profile.xml" + prefix: /profile + +#fos_user_register: + #resource: "@FOSUserBundle/Resources/config/routing/registration.xml" + #prefix: /register + +fos_user_resetting: + resource: "@FOSUserBundle/Resources/config/routing/resetting.xml" + prefix: /resetting + +fos_user_change_password: + resource: "@FOSUserBundle/Resources/config/routing/change_password.xml" + prefix: /profile \ No newline at end of file diff --git a/app/config/routing_dev.yml b/app/config/routing_dev.yml new file mode 100644 index 000000000..04520ab82 --- /dev/null +++ b/app/config/routing_dev.yml @@ -0,0 +1,17 @@ +_wdt: + resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" + prefix: /_wdt + +_profiler: + resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" + prefix: /_profiler + +_configurator: + resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml" + prefix: /_configurator + +_main: + resource: routing.yml + +# console: + # resource: "@CoreSphereConsoleBundle/Resources/config/routing.yml" \ No newline at end of file diff --git a/app/config/security.yml b/app/config/security.yml new file mode 100644 index 000000000..cb5d5244c --- /dev/null +++ b/app/config/security.yml @@ -0,0 +1,18 @@ +#app/config/security.yml +imports: + - { resource: "@LoginBundle/Resources/config/security.yml" } + +security: + + role_hierarchy: + ROLE_ADMIN: ROLE_USER + ROLE_SUPER_ADMIN: ROLE_ADMIN + + access_control: + - { path: ^/$, role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY } + #- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } + #- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/admin/, role: ROLE_ADMIN } + #- { path: ^/ajouter, role: ROLE_ADMIN } + - { path: ^/rule/, role: ROLE_USER } \ No newline at end of file diff --git a/app/console b/app/console new file mode 100644 index 000000000..fa6a36e24 --- /dev/null +++ b/app/console @@ -0,0 +1,27 @@ +#!/usr/bin/env php +getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); +$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; + +if ($debug) { + Debug::enable(); +} + +$kernel = new AppKernel($env, $debug); +$application = new Application($kernel); +$application->run($input); diff --git a/app/htaccess b/app/htaccess new file mode 100644 index 000000000..3418e55a6 --- /dev/null +++ b/app/htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/app/phpunit.xml.dist b/app/phpunit.xml.dist new file mode 100644 index 000000000..1e31086de --- /dev/null +++ b/app/phpunit.xml.dist @@ -0,0 +1,41 @@ + + + + + + + + ../src/*/*Bundle/Tests + ../src/*/Bundle/*Bundle/Tests + + + + + + + + ../src + + ../src/*/*Bundle/Resources + ../src/*/*Bundle/Tests + ../src/*/Bundle/*Bundle/Resources + ../src/*/Bundle/*Bundle/Tests + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..3a6a07a70 --- /dev/null +++ b/composer.json @@ -0,0 +1,96 @@ +{ + "name": "symfony/framework-standard-edition", + "license": "MIT", + "type": "project", + "description": "The \"Symfony Standard Edition\" distribution", + "autoload": { + "psr-0": { "": "src/", "SymfonyStandard": "app/" } + }, + "require": { + "php": ">=5.3.3", + "symfony/symfony": "2.8.9", + "doctrine/orm": "~2.2,>=2.2.3", + "doctrine/doctrine-bundle": "~1.2", + "twig/extensions": "~1.0", + "symfony/assetic-bundle": "~2.3", + "symfony/swiftmailer-bundle": "~2.3", + "symfony/monolog-bundle": "~2.4", + "symfony/expression-language": "~3.1", + "sensio/distribution-bundle": "~3.0,>=3.0.12", + "sensio/framework-extra-bundle": "~3.0", + "incenteev/composer-parameter-handler": "~2.0", + "stof/doctrine-extensions-bundle": "dev-master", + "friendsofsymfony/user-bundle": "~2.0@dev", + "jquery/jquery": "2.1.3", + "jquery/jquery_ui": "1.11.1", + "twitter/bootstrap": "dev-master#f9dd7466cfbc89c2ed458ec83cd29db7aa83525a", + "leafo/lessphp": "0.4.0", + "phpsec/phpsec":"0.6.*", + "white-october/pagerfanta-bundle": "dev-master", + "pagerfanta/pagerfanta": "dev-master", + "raulfraile/ladybug-bundle": "~1.0" + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "jquery/jquery", + "version": "2.1.3", + "dist": { + "url": "https://code.jquery.com/jquery-2.1.3.min.js", + "type": "file" + } + } + }, + { + "type": "package", + "package": { + "name": "jquery/jquery_ui", + "version": "1.11.1", + "dist": { + "url": "https://code.jquery.com/ui/1.11.1/jquery-ui.min.js", + "type": "file" + } + } + } + ], + "require-dev": { + "sensio/generator-bundle": "~2.3", + "doctrine/doctrine-fixtures-bundle": "^2.3" + }, + "scripts": { + "post-root-package-install": [ + "SymfonyStandard\\Composer::hookRootPackageInstall" + ], + "post-install-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles" + ], + "post-update-cmd": [ + "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles" + ] + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "symfony-app-dir": "app", + "symfony-web-dir": "web", + "symfony-assets-install": "relative", + "incenteev-parameters": { + "file": "app/config/parameters.yml" + }, + "branch-alias": { + "dev-master": "2.7-dev" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 000000000..6f6990068 --- /dev/null +++ b/composer.lock @@ -0,0 +1,3497 @@ +{ + "_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" + ], + "hash": "37ad8674f924dfb56dcf4358b751ec11", + "content-hash": "d183f5cfcbda3a4e72ebd38b3cc803f0", + "packages": [ + { + "name": "behat/transliterator", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "868e05be3a9f25ba6424c2dd4849567f50715003" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/868e05be3a9f25ba6424c2dd4849567f50715003", + "reference": "868e05be3a9f25ba6424c2dd4849567f50715003", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Transliterator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "time": "2015-09-28 16:26:35" + }, + { + "name": "doctrine/annotations", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/30e07cf03edc3cd3ef579d0dd4dd8c58250799a5", + "reference": "30e07cf03edc3cd3ef579d0dd4dd8c58250799a5", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2016-10-24 11:45:47" + }, + { + "name": "doctrine/cache", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2016-10-29 11:16:17" + }, + { + "name": "doctrine/collections", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2015-04-14 22:21:58" + }, + { + "name": "doctrine/common", + "version": "v2.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2015-12-25 13:18:31" + }, + { + "name": "doctrine/dbal", + "version": "v2.5.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", + "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.7-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*||^3.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2016-09-09 19:13:33" + }, + { + "name": "doctrine/doctrine-bundle", + "version": "1.6.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "dd40b0a7fb16658cda9def9786992b8df8a49be7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/dd40b0a7fb16658cda9def9786992b8df8a49be7", + "reference": "dd40b0a7fb16658cda9def9786992b8df8a49be7", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.3", + "doctrine/doctrine-cache-bundle": "~1.0", + "jdorn/sql-formatter": "~1.1", + "php": ">=5.3.2", + "symfony/console": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/doctrine-bridge": "~2.2|~3.0", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "phpunit/phpunit": "~4", + "satooshi/php-coveralls": "~0.6.1", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/property-info": "~2.8|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0", + "twig/twig": "~1.10" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "symfony/web-profiler-bundle": "To use the data collector." + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ], + "time": "2016-08-10 15:35:22" + }, + { + "name": "doctrine/doctrine-cache-bundle", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineCacheBundle.git", + "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/18c600a9b82f6454d2e81ca4957cdd56a1cf3504", + "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.4.2", + "doctrine/inflector": "~1.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": "~2.2|~3.0" + }, + "require-dev": { + "instaclick/coding-standard": "~1.1", + "instaclick/object-calisthenics-sniffs": "dev-master", + "instaclick/symfony2-coding-standard": "dev-remaster", + "phpunit/phpunit": "~4", + "predis/predis": "~0.8", + "satooshi/php-coveralls": "~0.6.1", + "squizlabs/php_codesniffer": "~1.5", + "symfony/console": "~2.2|~3.0", + "symfony/finder": "~2.2|~3.0", + "symfony/framework-bundle": "~2.2|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/security-acl": "~2.3|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0" + }, + "suggest": { + "symfony/security-acl": "For using this bundle to cache ACLs" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineCacheBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Fabio B. Silva", + "email": "fabio.bat.silva@gmail.com" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Bundle for Doctrine Cache", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2016-01-26 17:28:51" + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2015-11-06 14:35:42" + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09 13:34:57" + }, + { + "name": "doctrine/orm", + "version": "v2.5.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "73e4be7c7b3ba26f96b781a40b33feba4dfa6d45" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/73e4be7c7b3ba26f96b781a40b33feba4dfa6d45", + "reference": "73e4be7c7b3ba26f96b781a40b33feba4dfa6d45", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.4", + "doctrine/collections": "~1.2", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "ext-pdo": "*", + "php": ">=5.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "symfony/yaml": "~2.3|~3.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\ORM\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ], + "time": "2016-09-10 18:51:13" + }, + { + "name": "friendsofsymfony/user-bundle", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfSymfony/FOSUserBundle.git", + "reference": "3015f0965b74704362f9b2006646781d4613bcdd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfSymfony/FOSUserBundle/zipball/3015f0965b74704362f9b2006646781d4613bcdd", + "reference": "3015f0965b74704362f9b2006646781d4613bcdd", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0", + "symfony/form": "^2.7 || ^3.0", + "symfony/framework-bundle": "^2.7 || ^3.0", + "symfony/security-bundle": "^2.7 || ^3.0", + "symfony/twig-bundle": "^2.7 || ^3.0" + }, + "conflict": { + "symfony/doctrine-bridge": "<2.7" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^1.3", + "friendsofphp/php-cs-fixer": "^1.11", + "phpunit/phpunit": "~4.8|~5.0", + "swiftmailer/swiftmailer": "^4.3 || ^5.0", + "symfony/console": "^2.7 || ^3.0", + "symfony/phpunit-bridge": "^2.7 || ^3.0", + "symfony/validator": "^2.7 || ^3.0", + "symfony/yaml": "^2.7 || ^3.0", + "willdurand/propel-typehintable-behavior": "^1.0" + }, + "suggest": { + "willdurand/propel-typehintable-behavior": "Needed when using the propel implementation" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "FOS\\UserBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + }, + { + "name": "FriendsOfSymfony Community", + "homepage": "https://github.com/friendsofsymfony/FOSUserBundle/contributors" + }, + { + "name": "Thibault Duplessis", + "email": "thibault.duplessis@gmail.com" + } + ], + "description": "Symfony FOSUserBundle", + "homepage": "http://friendsofsymfony.github.com", + "keywords": [ + "User management" + ], + "time": "2016-11-08 13:35:42" + }, + { + "name": "gedmo/doctrine-extensions", + "version": "v2.4.24", + "source": { + "type": "git", + "url": "https://github.com/Atlantic18/DoctrineExtensions.git", + "reference": "a7f7243a19f79a99ac83a3570444d8795f665eef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Atlantic18/DoctrineExtensions/zipball/a7f7243a19f79a99ac83a3570444d8795f665eef", + "reference": "a7f7243a19f79a99ac83a3570444d8795f665eef", + "shasum": "" + }, + "require": { + "behat/transliterator": "~1.0", + "doctrine/common": "~2.4", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/common": ">=2.5.0", + "doctrine/mongodb-odm": ">=1.0.2", + "doctrine/orm": ">=2.5.0", + "phpunit/phpunit": "~4.4", + "phpunit/phpunit-mock-objects": "~2.3", + "symfony/yaml": "~2.6" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Gedmo\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Buchmann", + "email": "david@liip.ch" + }, + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + } + ], + "description": "Doctrine2 behavioral extensions", + "homepage": "http://gediminasm.org/", + "keywords": [ + "Blameable", + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "loggable", + "nestedset", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree", + "uploadable" + ], + "time": "2016-10-11 18:03:37" + }, + { + "name": "incenteev/composer-parameter-handler", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/Incenteev/ParameterHandler.git", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "reference": "d7ce7f06136109e81d1cb9d57066c4d4a99cf1cc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/yaml": "~2.3|~3.0" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpspec/prophecy-phpunit": "~1.0", + "symfony/filesystem": "~2.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Incenteev\\ParameterHandler\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Composer script handling your ignored parameter file", + "homepage": "https://github.com/Incenteev/ParameterHandler", + "keywords": [ + "parameters management" + ], + "time": "2015-11-10 17:04:01" + }, + { + "name": "ircmaxell/password-compat", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2014-11-20 16:49:30" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "time": "2014-01-12 16:20:24" + }, + { + "name": "jquery/jquery", + "version": "2.1.3", + "dist": { + "type": "file", + "url": "https://code.jquery.com/jquery-2.1.3.min.js", + "reference": null, + "shasum": null + }, + "type": "library" + }, + { + "name": "jquery/jquery_ui", + "version": "1.11.1", + "dist": { + "type": "file", + "url": "https://code.jquery.com/ui/1.11.1/jquery-ui.min.js", + "reference": null, + "shasum": null + }, + "type": "library" + }, + { + "name": "kriswallsmith/assetic", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/assetic.git", + "reference": "9928f7c4ad98b234e3559d1049abd13387f86db5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/9928f7c4ad98b234e3559d1049abd13387f86db5", + "reference": "9928f7c4ad98b234e3559d1049abd13387f86db5", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/process": "~2.1|~3.0" + }, + "conflict": { + "twig/twig": "<1.23" + }, + "require-dev": { + "cssmin/cssmin": "3.0.1", + "joliclic/javascript-packer": "1.1", + "kamicane/packager": "1.0", + "leafo/lessphp": "^0.3.7", + "leafo/scssphp": "~0.1", + "mrclay/minify": "~2.2", + "patchwork/jsqueeze": "~1.0|~2.0", + "phpunit/phpunit": "~4.8", + "psr/log": "~1.0", + "ptachoire/cssembed": "~1.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "twig/twig": "~1.8|~2.0" + }, + "suggest": { + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", + "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", + "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin", + "patchwork/jsqueeze": "Assetic provides the integration with the JSqueeze JavaScript compressor", + "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", + "twig/twig": "Assetic provides the integration with the Twig templating engine" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-0": { + "Assetic": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/kriswallsmith/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2015-11-12 13:51:40" + }, + { + "name": "leafo/lessphp", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/leafo/lessphp.git", + "reference": "51f3f06f0fe78a722dabfd14578444bdd078d9de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/leafo/lessphp/zipball/51f3f06f0fe78a722dabfd14578444bdd078d9de", + "reference": "51f3f06f0fe78a722dabfd14578444bdd078d9de", + "shasum": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.3-dev" + } + }, + "autoload": { + "classmap": [ + "lessc.inc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT", + "GPL-3.0" + ], + "authors": [ + { + "name": "Leaf Corcoran", + "email": "leafot@gmail.com", + "homepage": "http://leafo.net" + } + ], + "description": "lessphp is a compiler for LESS written in PHP.", + "homepage": "http://leafo.net/lessphp/", + "time": "2013-08-09 17:09:19" + }, + { + "name": "monolog/monolog", + "version": "1.21.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "~5.3" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2016-07-29 03:23:52" + }, + { + "name": "pagerfanta/pagerfanta", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/whiteoctober/Pagerfanta.git", + "reference": "63729ff5edd69072f0e34dc88d205975e2dd5164" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/63729ff5edd69072f0e34dc88d205975e2dd5164", + "reference": "63729ff5edd69072f0e34dc88d205975e2dd5164", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "doctrine/phpcr-odm": "1.*", + "jackalope/jackalope-doctrine-dbal": "1.*", + "jmikola/geojson": "~1.0", + "mandango/mandango": "~1.0@dev", + "mandango/mondator": "~1.0@dev", + "phpunit/phpunit": "~4", + "propel/propel1": "~1.6", + "ruflin/elastica": "~1.3", + "solarium/solarium": "~3.1" + }, + "suggest": { + "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.", + "doctrine/orm": "To use the DoctrineORMAdapter.", + "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0", + "mandango/mandango": "To use the MandangoAdapter.", + "propel/propel1": "To use the PropelAdapter", + "solarium/solarium": "To use the SolariumAdapter." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pagerfanta\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pablo Díez", + "email": "pablodip@gmail.com" + } + ], + "description": "Pagination for PHP 5.3", + "keywords": [ + "page", + "pagination", + "paginator", + "paging" + ], + "time": "2016-05-26 15:47:30" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2016-11-07 23:38:38" + }, + { + "name": "phpsec/phpsec", + "version": "0.6.5", + "source": { + "type": "git", + "url": "https://github.com/phpsec/phpSec.git", + "reference": "16c0c8b0f849a46e10159eb26e317a0cc5979481" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpsec/phpSec/zipball/16c0c8b0f849a46e10159eb26e317a0cc5979481", + "reference": "16c0c8b0f849a46e10159eb26e317a0cc5979481", + "shasum": "" + }, + "require": { + "php": ">=5.3.7", + "pimple/pimple": "~1.0" + }, + "suggest": { + "ext-mcrypt": "The Mcrypt extension is used to encrypt data.", + "ext-openssl": "The OpenSSL extension is used as a secure source of random data." + }, + "type": "library", + "autoload": { + "psr-0": { + "phpSec": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Audun Larsen", + "email": "larsen@xqus.com", + "homepage": "http://xqus.com", + "role": "Developer" + } + ], + "description": "A PHP security library", + "homepage": "https://phpseclib.com/", + "keywords": [ + "authy", + "cryptography", + "encryption", + "google auth", + "otp", + "security", + "session", + "yubikey" + ], + "time": "2014-11-03 18:35:42" + }, + { + "name": "pimple/pimple", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2013-11-22 08:30:29" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10 12:19:37" + }, + { + "name": "ptachoire/cssembed", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/krichprollsch/phpCssEmbed.git", + "reference": "406c6d5b846cafa9186f9944a6210d0e6fed154b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/krichprollsch/phpCssEmbed/zipball/406c6d5b846cafa9186f9944a6210d0e6fed154b", + "reference": "406c6d5b846cafa9186f9944a6210d0e6fed154b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "CssEmbed": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pierre Tachoire", + "email": "pierre.tachoire@gmail.com" + } + ], + "description": "Css url embed library.", + "homepage": "https://github.com/krichprollsch/phpCssEmbed", + "keywords": [ + "css", + "url" + ], + "time": "2013-07-22 20:01:48" + }, + { + "name": "raulfraile/ladybug", + "version": "v1.0.13", + "source": { + "type": "git", + "url": "https://github.com/raulfraile/ladybug.git", + "reference": "40a15f3d1c2644d7dcc3721c7609a32401494a9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/raulfraile/ladybug/zipball/40a15f3d1c2644d7dcc3721c7609a32401494a9f", + "reference": "40a15f3d1c2644d7dcc3721c7609a32401494a9f", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "ptachoire/cssembed": "v1.0.2", + "raulfraile/ladybug-plugin-extra": "~1.0.0", + "raulfraile/ladybug-theme-modern": ">=1.0.7,<2.0", + "symfony/config": ">=2.0", + "symfony/console": ">=2.0", + "symfony/dependency-injection": ">=2.0", + "symfony/finder": ">=2.0", + "twig/twig": ">=1.7.0" + }, + "require-dev": { + "jms/serializer": ">=0.11.0", + "mockery/mockery": "0.9.1", + "phpdocumentor/reflection-docblock": "2.0.0", + "phpunit/phpunit": "~4.0", + "symfony/dom-crawler": ">=2.0.4" + }, + "suggest": { + "jms/serializer": "Required to serialize data to XML and JSON", + "phpdocumentor/reflection-docblock": "Required to display object methods description", + "symfony/yaml": "Required to serialize data to YAML format" + }, + "type": "library", + "autoload": { + "psr-0": { + "Ladybug\\Theme": [ + "../ladybug-themes", + "data/themes" + ], + "Ladybug\\Plugin": [ + "../ladybug-plugins", + "data/plugins" + ], + "Ladybug": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raul Fraile", + "email": "raulfraile@gmail.com", + "homepage": "http://www.raulfb.com/", + "role": "Developer" + } + ], + "description": "Simple and Extensible PHP Dumper", + "homepage": "https://github.com/raulfraile/ladybug", + "keywords": [ + "dump", + "dumper", + "ladybug", + "print_r", + "var_dump" + ], + "time": "2015-12-23 08:33:11" + }, + { + "name": "raulfraile/ladybug-bundle", + "version": "v1.0.6", + "target-dir": "RaulFraile/Bundle/LadybugBundle", + "source": { + "type": "git", + "url": "https://github.com/raulfraile/LadybugBundle.git", + "reference": "0a3171f1d15194aadaba12fd9c81c11707c6b1c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/raulfraile/LadybugBundle/zipball/0a3171f1d15194aadaba12fd9c81c11707c6b1c3", + "reference": "0a3171f1d15194aadaba12fd9c81c11707c6b1c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "raulfraile/ladybug": "~1.0.0", + "raulfraile/ladybug-plugin-symfony2": "~1.0.0", + "symfony/framework-bundle": ">=2.0.0" + }, + "replace": { + "raulfraile/ladybugbundle": "dev-master" + }, + "require-dev": { + "mockery/mockery": "@stable", + "phpunit/phpunit": "@stable" + }, + "type": "symfony-bundle", + "autoload": { + "psr-0": { + "RaulFraile\\Bundle\\LadybugBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raul Fraile", + "email": "raulfraile@gmail.com", + "homepage": "http://www.raulfrailebeneyto.com/" + } + ], + "description": "Symfony2 bundle for Ladybug library (Simple and Extensible PHP Dumper)", + "homepage": "https://github.com/raulfraile/LadybugBundle", + "keywords": [ + "dump", + "ladybug", + "tree", + "twig" + ], + "time": "2014-10-10 06:09:15" + }, + { + "name": "raulfraile/ladybug-installer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/raulfraile/ladybug-installer.git", + "reference": "04f38f50cac673d3ca93de9c0805889e37804b33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/raulfraile/ladybug-installer/zipball/04f38f50cac673d3ca93de9c0805889e37804b33", + "reference": "04f38f50cac673d3ca93de9c0805889e37804b33", + "shasum": "" + }, + "require": { + "composer-plugin-api": "*" + }, + "require-dev": { + "composer/composer": "1.*", + "mockery/mockery": "0.8.0" + }, + "type": "composer-plugin", + "extra": { + "class": "\\Ladybug\\LadybugPlugin" + }, + "autoload": { + "psr-0": { + "Ladybug": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Unified installer for ladybug addons", + "keywords": [ + "installer", + "ladybug", + "plugins", + "themes" + ], + "time": "2013-09-21 23:28:23" + }, + { + "name": "raulfraile/ladybug-plugin-extra", + "version": "v1.0.1", + "target-dir": "Ladybug/Plugin/Extra", + "source": { + "type": "git", + "url": "https://github.com/raulfraile/ladybug-plugin-extra.git", + "reference": "d240770a4c610e26657e5edc0edaace6d07187b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/raulfraile/ladybug-plugin-extra/zipball/d240770a4c610e26657e5edc0edaace6d07187b2", + "reference": "d240770a4c610e26657e5edc0edaace6d07187b2", + "shasum": "" + }, + "require": { + "raulfraile/ladybug-installer": "~1.0" + }, + "require-dev": { + "mockery/mockery": "0.8.0", + "raulfraile/ladybug": "~1.0.0" + }, + "type": "ladybug-plugin", + "extra": { + "ladybug_name": "Extra" + }, + "autoload": { + "psr-0": { + "Ladybug\\Plugin\\Extra\\": "" + }, + "files": [ + "Resources/php/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Extra plugin for ladybug", + "keywords": [ + "ladybug", + "plugin" + ], + "time": "2013-09-29 10:19:08" + }, + { + "name": "raulfraile/ladybug-plugin-symfony2", + "version": "v1.0.1", + "target-dir": "Ladybug/Plugin/Symfony2", + "source": { + "type": "git", + "url": "https://github.com/raulfraile/ladybug-plugin-symfony2.git", + "reference": "81a50bdcf966ccf51e9dbf07494fdb3a4bf69b84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/raulfraile/ladybug-plugin-symfony2/zipball/81a50bdcf966ccf51e9dbf07494fdb3a4bf69b84", + "reference": "81a50bdcf966ccf51e9dbf07494fdb3a4bf69b84", + "shasum": "" + }, + "require": { + "raulfraile/ladybug-installer": "~1.0.0" + }, + "require-dev": { + "mockery/mockery": "0.8.0", + "raulfraile/ladybug": "~1.0.0", + "symfony/http-foundation": "*" + }, + "type": "ladybug-plugin", + "extra": { + "ladybug_name": "Symfony2" + }, + "autoload": { + "psr-0": { + "Ladybug\\Plugin\\Symfony2\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Symfony2 plugin for ladybug", + "keywords": [ + "ladybug", + "plugin" + ], + "time": "2013-09-29 09:17:09" + }, + { + "name": "raulfraile/ladybug-theme-modern", + "version": "v1.0.7", + "source": { + "type": "git", + "url": "https://github.com/raulfraile/ladybug-theme-modern.git", + "reference": "0271ed62bc45397be9e346c9c19a0ae0d811ef8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/raulfraile/ladybug-theme-modern/zipball/0271ed62bc45397be9e346c9c19a0ae0d811ef8e", + "reference": "0271ed62bc45397be9e346c9c19a0ae0d811ef8e", + "shasum": "" + }, + "require": { + "raulfraile/ladybug-installer": "~1.0.0" + }, + "type": "ladybug-theme", + "extra": { + "ladybug_name": "Modern" + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Theme 'modern' for ladybug", + "keywords": [ + "dump", + "ladybug", + "print_r", + "theme", + "var_dump" + ], + "time": "2014-08-21 08:27:46" + }, + { + "name": "sensio/distribution-bundle", + "version": "v3.0.36", + "target-dir": "Sensio/Bundle/DistributionBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", + "reference": "964a56e855acac38d4a81920b3a86543f7e8492f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/964a56e855acac38d4a81920b3a86543f7e8492f", + "reference": "964a56e855acac38d4a81920b3a86543f7e8492f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sensiolabs/security-checker": "~3.0", + "symfony/class-loader": "~2.2", + "symfony/framework-bundle": "~2.3", + "symfony/process": "~2.2" + }, + "require-dev": { + "symfony/form": "~2.2", + "symfony/validator": "~2.2", + "symfony/yaml": "~2.2" + }, + "suggest": { + "symfony/form": "If you want to use the configurator", + "symfony/validator": "If you want to use the configurator", + "symfony/yaml": "If you want to use the configurator" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Sensio\\Bundle\\DistributionBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Base bundle for Symfony Distributions", + "keywords": [ + "configuration", + "distribution" + ], + "time": "2016-04-25 20:46:43" + }, + { + "name": "sensio/framework-extra-bundle", + "version": "v3.0.16", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "507a15f56fa7699f6cc8c2c7de4080b19ce22546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/507a15f56fa7699f6cc8c2c7de4080b19ce22546", + "reference": "507a15f56fa7699f6cc8c2c7de4080b19ce22546", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.2", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "symfony/browser-kit": "~2.3|~3.0", + "symfony/dom-crawler": "~2.3|~3.0", + "symfony/expression-language": "~2.4|~3.0", + "symfony/finder": "~2.3|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/security-bundle": "~2.4|~3.0", + "symfony/twig-bundle": "~2.3|~3.0", + "twig/twig": "~1.11|~2.0" + }, + "suggest": { + "symfony/expression-language": "", + "symfony/psr-http-message-bridge": "To use the PSR-7 converters", + "symfony/security-bundle": "" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sensio\\Bundle\\FrameworkExtraBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": [ + "annotations", + "controllers" + ], + "time": "2016-03-25 17:08:27" + }, + { + "name": "sensiolabs/security-checker", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/security-checker.git", + "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/21696b0daa731064c23cfb694c60a2584a7b6e93", + "reference": "21696b0daa731064c23cfb694c60a2584a7b6e93", + "shasum": "" + }, + "require": { + "symfony/console": "~2.0|~3.0" + }, + "bin": [ + "security-checker" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-0": { + "SensioLabs\\Security": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "A security checker for your composer.lock", + "time": "2015-11-07 08:07:40" + }, + { + "name": "stof/doctrine-extensions-bundle", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/stof/StofDoctrineExtensionsBundle.git", + "reference": "4e7499d25dc5d0862da09fa8e336164948a29a25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stof/StofDoctrineExtensionsBundle/zipball/4e7499d25dc5d0862da09fa8e336164948a29a25", + "reference": "4e7499d25dc5d0862da09fa8e336164948a29a25", + "shasum": "" + }, + "require": { + "gedmo/doctrine-extensions": "^2.3.1", + "php": ">=5.3.2", + "symfony/framework-bundle": "~2.1|~3.0" + }, + "suggest": { + "doctrine/doctrine-bundle": "to use the ORM extensions", + "doctrine/mongodb-odm-bundle": "to use the MongoDB ODM extensions" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Stof\\DoctrineExtensionsBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + } + ], + "description": "Integration of the gedmo/doctrine-extensions with Symfony2", + "homepage": "https://github.com/stof/StofDoctrineExtensionsBundle", + "keywords": [ + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "loggable", + "nestedset", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree" + ], + "time": "2016-01-26 23:58:32" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.3", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", + "reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "http://swiftmailer.org", + "keywords": [ + "email", + "mail", + "mailer" + ], + "time": "2016-07-08 11:51:25" + }, + { + "name": "symfony/assetic-bundle", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/assetic-bundle.git", + "reference": "aa5b4f8b712f38745928fa845ddb73300bb2af6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/aa5b4f8b712f38745928fa845ddb73300bb2af6d", + "reference": "aa5b4f8b712f38745928fa845ddb73300bb2af6d", + "shasum": "" + }, + "require": { + "kriswallsmith/assetic": "~1.3", + "php": ">=5.3.0", + "symfony/console": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "conflict": { + "kriswallsmith/spork": "<=0.2", + "twig/twig": "<1.20" + }, + "require-dev": { + "kriswallsmith/spork": "~0.3", + "patchwork/jsqueeze": "~1.0", + "symfony/class-loader": "~2.3|~3.0", + "symfony/css-selector": "~2.3|~3.0", + "symfony/dom-crawler": "~2.3|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/twig-bundle": "~2.3|~3.0" + }, + "suggest": { + "kriswallsmith/spork": "to be able to dump assets in parallel", + "symfony/twig-bundle": "to use the Twig integration" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\AsseticBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Integrates Assetic into Symfony2", + "homepage": "https://github.com/symfony/AsseticBundle", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2015-12-28 13:12:39" + }, + { + "name": "symfony/expression-language", + "version": "v3.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "5f75588068396b7802caccb88f806cca494e7974" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/5f75588068396b7802caccb88f806cca494e7974", + "reference": "5f75588068396b7802caccb88f806cca494e7974", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "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 ExpressionLanguage Component", + "homepage": "https://symfony.com", + "time": "2016-09-06 11:02:40" + }, + { + "name": "symfony/monolog-bundle", + "version": "2.12.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "6acef3bd201c4f35e42e52dedf1fe088f30e07e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/6acef3bd201c4f35e42e52dedf1fe088f30e07e8", + "reference": "6acef3bd201c4f35e42e52dedf1fe088f30e07e8", + "shasum": "" + }, + "require": { + "monolog/monolog": "~1.18", + "php": ">=5.3.2", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/monolog-bridge": "~2.3|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "symfony/console": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony MonologBundle", + "homepage": "http://symfony.com", + "keywords": [ + "log", + "logging" + ], + "time": "2016-11-06 18:54:50" + }, + { + "name": "symfony/polyfill-apcu", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-apcu.git", + "reference": "6d58bceaeea2c2d3eb62503839b18646e161cd6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/6d58bceaeea2c2d3eb62503839b18646e161cd6b", + "reference": "6d58bceaeea2c2d3eb62503839b18646e161cd6b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "apcu", + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "0f8dc2c45f69f8672379e9210bca4a115cd5146f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/0f8dc2c45f69f8672379e9210bca4a115cd5146f", + "reference": "0f8dc2c45f69f8672379e9210bca4a115cd5146f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "dff51f72b0706335131b00a7f49606168c582594" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-php54", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php54.git", + "reference": "34d761992f6f2cc6092cc0e5e93f38b53ba5e4f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/34d761992f6f2cc6092cc0e5e93f38b53ba5e4f1", + "reference": "34d761992f6f2cc6092cc0e5e93f38b53ba5e4f1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php54\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-php55", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php55.git", + "reference": "bf2ff9ad6be1a4772cb873e4eea94d70daa95c6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/bf2ff9ad6be1a4772cb873e4eea94d70daa95c6d", + "reference": "bf2ff9ad6be1a4772cb873e4eea94d70daa95c6d", + "shasum": "" + }, + "require": { + "ircmaxell/password-compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php55\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "3edf57a8fbf9a927533344cef65ad7e1cf31030a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/3edf57a8fbf9a927533344cef65ad7e1cf31030a", + "reference": "3edf57a8fbf9a927533344cef65ad7e1cf31030a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "a42f4b6b05ed458910f8af4c4e1121b0101b2d85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/a42f4b6b05ed458910f8af4c4e1121b0101b2d85", + "reference": "a42f4b6b05ed458910f8af4c4e1121b0101b2d85", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/polyfill-util", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "ef830ce3d218e622b221d6bfad42c751d974bf99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/ef830ce3d218e622b221d6bfad42c751d974bf99", + "reference": "ef830ce3d218e622b221d6bfad42c751d974bf99", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2016-05-18 14:26:46" + }, + { + "name": "symfony/security-acl", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-acl.git", + "reference": "053b49bf4aa333a392c83296855989bcf88ddad1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-acl/zipball/053b49bf4aa333a392c83296855989bcf88ddad1", + "reference": "053b49bf4aa333a392c83296855989bcf88ddad1", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/security-core": "~2.8|~3.0" + }, + "require-dev": { + "doctrine/common": "~2.2", + "doctrine/dbal": "~2.2", + "psr/log": "~1.0", + "symfony/phpunit-bridge": "~2.8|~3.0" + }, + "suggest": { + "doctrine/dbal": "For using the built-in ACL implementation", + "symfony/class-loader": "For using the ACL generateSql script", + "symfony/finder": "For using the ACL generateSql script" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Acl\\": "" + }, + "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 Security Component - ACL (Access Control List)", + "homepage": "https://symfony.com", + "time": "2015-12-28 09:39:46" + }, + { + "name": "symfony/swiftmailer-bundle", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/swiftmailer-bundle.git", + "reference": "d7b7bd6bb6e9b32ebc5f9778f94d4b4e4af5d069" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/d7b7bd6bb6e9b32ebc5f9778f94d4b4e4af5d069", + "reference": "d7b7bd6bb6e9b32ebc5f9778f94d4b4e4af5d069", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "swiftmailer/swiftmailer": ">=4.2.0,~5.0", + "symfony/config": "~2.7|~3.0", + "symfony/dependency-injection": "~2.7|~3.0", + "symfony/http-kernel": "~2.7|~3.0" + }, + "require-dev": { + "symfony/console": "~2.7|~3.0", + "symfony/framework-bundle": "~2.7|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/yaml": "~2.7|~3.0" + }, + "suggest": { + "psr/log": "Allows logging" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SwiftmailerBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony SwiftmailerBundle", + "homepage": "http://symfony.com", + "time": "2016-10-27 17:59:30" + }, + { + "name": "symfony/symfony", + "version": "v2.8.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/symfony.git", + "reference": "df02dd5d3f7decb3a05c6d0f31054b4263625dcb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/symfony/zipball/df02dd5d3f7decb3a05c6d0f31054b4263625dcb", + "reference": "df02dd5d3f7decb3a05c6d0f31054b4263625dcb", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.4", + "php": ">=5.3.9", + "psr/log": "~1.0", + "symfony/polyfill-apcu": "~1.1", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php54": "~1.0", + "symfony/polyfill-php55": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0", + "symfony/security-acl": "~2.7|~3.0.0", + "twig/twig": "~1.23|~2.0" + }, + "conflict": { + "phpdocumentor/reflection": "<1.0.7" + }, + "replace": { + "symfony/asset": "self.version", + "symfony/browser-kit": "self.version", + "symfony/class-loader": "self.version", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/css-selector": "self.version", + "symfony/debug": "self.version", + "symfony/debug-bundle": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/expression-language": "self.version", + "symfony/filesystem": "self.version", + "symfony/finder": "self.version", + "symfony/form": "self.version", + "symfony/framework-bundle": "self.version", + "symfony/http-foundation": "self.version", + "symfony/http-kernel": "self.version", + "symfony/intl": "self.version", + "symfony/ldap": "self.version", + "symfony/locale": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/property-access": "self.version", + "symfony/property-info": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/security-core": "self.version", + "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", + "symfony/security-http": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/swiftmailer-bridge": "self.version", + "symfony/templating": "self.version", + "symfony/translation": "self.version", + "symfony/twig-bridge": "self.version", + "symfony/twig-bundle": "self.version", + "symfony/validator": "self.version", + "symfony/var-dumper": "self.version", + "symfony/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.4", + "doctrine/doctrine-bundle": "~1.2", + "doctrine/orm": "~2.4,>=2.4.5", + "egulias/email-validator": "~1.2,>=1.2.1", + "monolog/monolog": "~1.11", + "ocramius/proxy-manager": "~0.4|~1.0|~2.0", + "phpdocumentor/reflection": "^1.0.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", + "Symfony\\Bundle\\": "src/Symfony/Bundle/", + "Symfony\\Component\\": "src/Symfony/Component/" + }, + "classmap": [ + "src/Symfony/Component/Intl/Resources/stubs" + ], + "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": "The Symfony PHP framework", + "homepage": "https://symfony.com", + "keywords": [ + "framework" + ], + "time": "2016-07-30 08:48:52" + }, + { + "name": "twig/extensions", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig-extensions.git", + "reference": "f0bb8431c8691f5a39f1017d9a5967a082bf01ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/f0bb8431c8691f5a39f1017d9a5967a082bf01ff", + "reference": "f0bb8431c8691f5a39f1017d9a5967a082bf01ff", + "shasum": "" + }, + "require": { + "twig/twig": "~1.20|~2.0" + }, + "require-dev": { + "symfony/translation": "~2.3" + }, + "suggest": { + "symfony/translation": "Allow the time_diff output to be translated" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_Extensions_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Common additional features for Twig that do not directly belong in core", + "homepage": "http://twig.sensiolabs.org/doc/extensions/index.html", + "keywords": [ + "i18n", + "text" + ], + "time": "2016-10-25 17:34:14" + }, + { + "name": "twig/twig", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97", + "reference": "3c6c0033fd3b5679c6e1cb60f4f9766c2b424d97", + "shasum": "" + }, + "require": { + "php": ">=5.2.7" + }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.27-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "http://twig.sensiolabs.org/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2016-10-25 19:17:17" + }, + { + "name": "twitter/bootstrap", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/twbs/bootstrap.git", + "reference": "f9dd7466cfbc89c2ed458ec83cd29db7aa83525a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twbs/bootstrap/zipball/f9dd7466cfbc89c2ed458ec83cd29db7aa83525a", + "reference": "f9dd7466cfbc89c2ed458ec83cd29db7aa83525a", + "shasum": "" + }, + "replace": { + "twitter/bootstrap": "self.version" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jacob Thornton", + "email": "jacobthornton@gmail.com" + }, + { + "name": "Mark Otto", + "email": "markdotto@gmail.com" + } + ], + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "homepage": "http://getbootstrap.com", + "keywords": [ + "JS", + "css", + "framework", + "front-end", + "less", + "mobile-first", + "responsive", + "web" + ], + "time": "2013-10-01 01:11:20" + }, + { + "name": "white-october/pagerfanta-bundle", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle.git", + "reference": "394c0a2d730a0ef86666bc22081ca1fcd1ec8edd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/whiteoctober/WhiteOctoberPagerfantaBundle/zipball/394c0a2d730a0ef86666bc22081ca1fcd1ec8edd", + "reference": "394c0a2d730a0ef86666bc22081ca1fcd1ec8edd", + "shasum": "" + }, + "require": { + "pagerfanta/pagerfanta": "1.0.*", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/property-access": "~2.3|~3.0", + "symfony/twig-bundle": "~2.3|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~3.7", + "symfony/symfony": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "WhiteOctober\\PagerfantaBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pablo Díez", + "email": "pablodip@gmail.com" + } + ], + "description": "Bundle to use Pagerfanta with Symfony2", + "keywords": [ + "page", + "paging" + ], + "time": "2016-10-10 07:46:28" + } + ], + "packages-dev": [ + { + "name": "doctrine/data-fixtures", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "b3cae5efef97191a08d53d733260f7eb667c16e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/b3cae5efef97191a08d53d733260f7eb667c16e4", + "reference": "b3cae5efef97191a08d53d733260f7eb667c16e4", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.2", + "php": "^5.6 || ^7.0" + }, + "conflict": { + "doctrine/orm": "< 2.4" + }, + "require-dev": { + "doctrine/dbal": "^2.5.4", + "doctrine/orm": "^2.5.4", + "phpunit/phpunit": "^5.4.6" + }, + "suggest": { + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\DataFixtures": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database" + ], + "time": "2016-06-20 18:08:26" + }, + { + "name": "doctrine/doctrine-fixtures-bundle", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineFixturesBundle.git", + "reference": "0f1a2f91b349e10f5c343f75ab71d23aace5b029" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/0f1a2f91b349e10f5c343f75ab71d23aace5b029", + "reference": "0f1a2f91b349e10f5c343f75ab71d23aace5b029", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "~1.0", + "doctrine/doctrine-bundle": "~1.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\FixturesBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DoctrineFixturesBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "Fixture", + "persistence" + ], + "time": "2015-11-04 21:23:23" + }, + { + "name": "sensio/generator-bundle", + "version": "v2.5.3", + "target-dir": "Sensio/Bundle/GeneratorBundle", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioGeneratorBundle.git", + "reference": "e50108c2133ee5c9c484555faed50c17a61221d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioGeneratorBundle/zipball/e50108c2133ee5c9c484555faed50c17a61221d3", + "reference": "e50108c2133ee5c9c484555faed50c17a61221d3", + "shasum": "" + }, + "require": { + "symfony/console": "~2.5", + "symfony/framework-bundle": "~2.2" + }, + "require-dev": { + "doctrine/orm": "~2.2,>=2.2.3", + "symfony/doctrine-bridge": "~2.2", + "twig/twig": "~1.11" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Sensio\\Bundle\\GeneratorBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle generates code for you", + "time": "2015-03-17 06:36:52" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "stof/doctrine-extensions-bundle": 20, + "friendsofsymfony/user-bundle": 20, + "twitter/bootstrap": 20, + "white-october/pagerfanta-bundle": 20, + "pagerfanta/pagerfanta": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": [] +} diff --git a/src/.htaccess b/src/.htaccess new file mode 100644 index 000000000..3418e55a6 --- /dev/null +++ b/src/.htaccess @@ -0,0 +1 @@ +deny from all \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/Controller/DefaultController.php b/src/Myddleware/LoginBundle/Controller/DefaultController.php new file mode 100644 index 000000000..03b5bf6b2 --- /dev/null +++ b/src/Myddleware/LoginBundle/Controller/DefaultController.php @@ -0,0 +1,131 @@ +getSession(); + $csrfToken = $this->container->has('form.csrf_provider') + ? $this->container->get('form.csrf_provider')->generateCsrfToken('authenticate') + : null; + + // get the error if any (works with forward and redirect -- see below) + if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR); + } elseif (null !== $session && $session->has(SecurityContext::AUTHENTICATION_ERROR)) { + $error = $session->get(SecurityContext::AUTHENTICATION_ERROR); + $session->remove(SecurityContext::AUTHENTICATION_ERROR); + } else { + $error = ''; + } + + if ($error) { + // TODO: this is a potential security risk (see http://trac.symfony-project.org/ticket/9523) + $error = $error->getMessage(); + } + // last username entered by the user + $lastUsername = (null === $session) ? '' : $session->get(SecurityContext::LAST_USERNAME); + + $securityContext = $this->get('security.context'); + if( $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') ){ + return $this->redirect( $this->generateUrl('regle_panel') ); + } + else { + + $this->calculBan( $lastUsername ); + + $attempt = ((isset($_SESSION['myddleware']['secure'][$lastUsername]['attempt'])) ? $_SESSION['myddleware']['secure'][$lastUsername]['attempt'] : 0 ); + $remaining = ((isset($_SESSION['myddleware']['secure'][$lastUsername]['remaining'])) ? $_SESSION['myddleware']['secure'][$lastUsername]['remaining'] : 0 ); + + return $this->render('LoginBundle:Default:index.html.twig',array( + 'last_username' => $lastUsername, + 'error' => $error, + 'csrf_token' => $csrfToken, + 'attempt' => $attempt, + 'remaining' => $remaining + )); + + } + } + + private function calculBan($lastUsername) { + + if(isset($_SESSION['myddleware']['secure'][$lastUsername]['time'])) { + if(time() > $_SESSION['myddleware']['secure'][$lastUsername]['time']) { + $_SESSION['myddleware']['secure'][$lastUsername]['attempt'] = 1; + } + else { + + // RESTE X MINUTES AVANT LA FUTUR CONNEXION + $date1 = time(); + $date2 = $_SESSION['myddleware']['secure'][$lastUsername]['time']; + $diff = abs($date1 - $date2); + + $diff = abs($date1 - $date2); // abs pour avoir la valeur absolute, ainsi éviter d'avoir une différence négative + $remaining = array(); + + $tmp = $diff; + $remaining['second'] = $tmp % 60; + + $tmp = floor( ($tmp - $remaining['second']) /60 ); + $remaining['minute'] = $tmp % 60; + + $tmp = floor( ($tmp - $remaining['minute'])/60 ); + $remaining['hour'] = $tmp % 24; + + $tmp = floor( ($tmp - $remaining['hour']) /24 ); + $remaining['day'] = $tmp; + + $_SESSION['myddleware']['secure'][$lastUsername]['remaining'] = $remaining; + } + } + } + + public function verifAccountAction(Request $request) { + + try { + if ($request->isMethod('POST')) { + + $lastUsername = trim($this->getRequest()->request->get('login')); + + // contrôle des tentatives + // si le nombre de tentative n'existe pas on affecte 0 + if(!isset($_SESSION['myddleware']['secure'][$lastUsername]['attempt'])) { + $_SESSION['myddleware']['secure'][$lastUsername]['attempt'] = 1; + } + else { // si existe on ajoute +1 + $_SESSION['myddleware']['secure'][$lastUsername]['attempt']++; + } + + // si le nombre de tentative est supérieur à 5 alors on ajoute une date de contrôle + if($_SESSION['myddleware']['secure'][$lastUsername]['attempt'] > 4 ) { + + if(!isset($_SESSION['myddleware']['secure'][$lastUsername]['time'])) { + $_SESSION['myddleware']['secure'][$lastUsername]['time'] = strtotime("+15 minutes", time()); + } + else { + $this->calculBan( $lastUsername ); + } + } + return new Response(1); + } + else { + return new Response(0); + } + } + catch(Exception $e) { + return new Response(0); + } + + } + +} diff --git a/src/Myddleware/LoginBundle/DependencyInjection/Configuration.php b/src/Myddleware/LoginBundle/DependencyInjection/Configuration.php new file mode 100644 index 000000000..6effb19ed --- /dev/null +++ b/src/Myddleware/LoginBundle/DependencyInjection/Configuration.php @@ -0,0 +1,29 @@ +root('hello'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/src/Myddleware/LoginBundle/DependencyInjection/HelloExtension.php b/src/Myddleware/LoginBundle/DependencyInjection/HelloExtension.php new file mode 100644 index 000000000..bb6d03ac2 --- /dev/null +++ b/src/Myddleware/LoginBundle/DependencyInjection/HelloExtension.php @@ -0,0 +1,52 @@ +. +*********************************************************************************/ + + +namespace Tuto\HelloBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader; + +/** + * This is the class that loads and manages your bundle configuration + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} + */ +class HelloExtension extends Extension +{ + /** + * {@inheritDoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $this->processConfiguration($configuration, $configs); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + } +} diff --git a/src/Myddleware/LoginBundle/Entity/User.php b/src/Myddleware/LoginBundle/Entity/User.php new file mode 100644 index 000000000..254e0da66 --- /dev/null +++ b/src/Myddleware/LoginBundle/Entity/User.php @@ -0,0 +1,54 @@ +. +*********************************************************************************/ + +namespace Myddleware\LoginBundle\Entity; + +use FOS\UserBundle\Model\User as BaseUser; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ORM\Entity + * @ORM\Table(name="users") + */ +class User extends BaseUser +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + * @ORM\GeneratedValue(strategy="AUTO") + */ + protected $id; + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } +} \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/Entity/User.php~ b/src/Myddleware/LoginBundle/Entity/User.php~ new file mode 100644 index 000000000..351a01f11 --- /dev/null +++ b/src/Myddleware/LoginBundle/Entity/User.php~ @@ -0,0 +1,31 @@ +id; + } +} \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/LoginBundle.php b/src/Myddleware/LoginBundle/LoginBundle.php new file mode 100644 index 000000000..67ac10ffb --- /dev/null +++ b/src/Myddleware/LoginBundle/LoginBundle.php @@ -0,0 +1,10 @@ +. +*********************************************************************************/ + +#myd_connexion { + margin: 100px auto; + width: 700px; + height: 400px; +} + +#myd_connexion div { + height: 350px; + float: left; + padding: 20px; +} + +#myd_connexion .grey, #myd_connexion .white { + -webkit-box-shadow: 1px 1px 9px 0px rgba(50, 50, 50, 0.75); + -moz-box-shadow: 1px 1px 9px 0px rgba(50, 50, 50, 0.75); + box-shadow: 1px 1px 9px 0px rgba(50, 50, 50, 0.75); +} + +#myd_connexion .grey { + width: 320px; + min-width: 320px; + background: #404042; + color: white; + padding-top: 20%; +} + +#myd_connexion .white { + width: 300px; + min-width: 300px; + background: white; +} + +#myd_connexion .white .form-inline .form-control { + width: 196px; +} + +#myd_connexion .logo { + width:160px; + height:113px; + display:inline-block; +} + +#myd_connexion .form-inline { + display:inline-block; +} + +#myd_connexion .alert { + height: 27px; + padding: 4px; + width: 196px; + word-spacing: -0.6em; +} + +.btn-myd, .btn-mydinv, .btn-mydinv:hover, .btn-myd:hover { + color: #404042; + font-weight: bold; + height: 31px; + width: 196px; + border: none; + display: inline-block; + line-height: 2em; + text-align: center; + cursor:pointer; +} + +.clr { + clear: both; +} + +.ctr { + text-align: center; +} \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/Resources/public/images/btn.png b/src/Myddleware/LoginBundle/Resources/public/images/btn.png new file mode 100644 index 000000000..e74f2f1e9 Binary files /dev/null and b/src/Myddleware/LoginBundle/Resources/public/images/btn.png differ diff --git a/src/Myddleware/LoginBundle/Resources/public/images/fd.jpg b/src/Myddleware/LoginBundle/Resources/public/images/fd.jpg new file mode 100644 index 000000000..095f5b043 Binary files /dev/null and b/src/Myddleware/LoginBundle/Resources/public/images/fd.jpg differ diff --git a/src/Myddleware/LoginBundle/Resources/public/images/logo.png b/src/Myddleware/LoginBundle/Resources/public/images/logo.png new file mode 100644 index 000000000..e326ba551 Binary files /dev/null and b/src/Myddleware/LoginBundle/Resources/public/images/logo.png differ diff --git a/src/Myddleware/LoginBundle/Resources/public/js/login.js b/src/Myddleware/LoginBundle/Resources/public/js/login.js new file mode 100644 index 000000000..21393a9a1 --- /dev/null +++ b/src/Myddleware/LoginBundle/Resources/public/js/login.js @@ -0,0 +1,76 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +$(document).ready(function() { + + $('#myd_connexion .load').hide(); + $('#_error').hide(); + + + $("#control").one( "click", function() { + if( $('#password').val() != '' && $('#username').val() != '') { + controleLogin(); + } + } ); + + $('form').keypress(function(e){ + if( e.which == 13 ){ + if( $('#password').val() != '' && $('#username').val() != '') { + $("#control").click(); + return false; + } else { + return false; + } + } + }); + +}); + + +function controleLogin() { + + $.ajax({ + type: "POST", + url: path_control, + data:{ + login : $('#username').val() + }, + beforeSend: function() { + //$('#_error').fadeOut(); + //$('#control').hide(); + //$('#_submit').show(); + }, + success: function(data){ + if(data == 1) { + $('#_error').fadeOut(); + $('#_submit').click(); + } + else { + $('#control').show(); + $('#_error').show(); + $('#_submit').hide(); + } + } + }); +} diff --git a/src/Myddleware/LoginBundle/Resources/translations/messages.en.yml b/src/Myddleware/LoginBundle/Resources/translations/messages.en.yml new file mode 100644 index 000000000..a7c6e8117 --- /dev/null +++ b/src/Myddleware/LoginBundle/Resources/translations/messages.en.yml @@ -0,0 +1,13 @@ +login: + title: Connection + index: Home + user: Login + pwd: Password + connexion: Sign in + error_session: Error of session + text_up_right: More information about myddleware + text_bottom_right: You will be redirected towards our site + btn: + here: Click here + create: create an account + pwd_forgot: Forgot your password ? \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/Resources/translations/messages.fr.yml b/src/Myddleware/LoginBundle/Resources/translations/messages.fr.yml new file mode 100644 index 000000000..0a5841b84 --- /dev/null +++ b/src/Myddleware/LoginBundle/Resources/translations/messages.fr.yml @@ -0,0 +1,13 @@ +login: + title: Connexion + index: Accueil + user: Identifiant + pwd: Mot de passe + connexion: Se Connecter + error_session: Erreur de session + text_up_right: Pour en savoir plus sur myddleware + text_bottom_right: Vous serez redirigé vers notre site explicatif + btn: + here: Cliquez ici + create: Créer un compte + pwd_forgot: Mot de passe oublié ? \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/Resources/views/Default/index.html.twig b/src/Myddleware/LoginBundle/Resources/views/Default/index.html.twig new file mode 100644 index 000000000..98fd68a94 --- /dev/null +++ b/src/Myddleware/LoginBundle/Resources/views/Default/index.html.twig @@ -0,0 +1,143 @@ +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ #} + +{% extends "::baseone.html.twig" %} + +{# {% trans_default_domain 'FOSUserBundle' %} #} + +{% block title %}{{parent()}} | {{ 'login.title'|trans }}{% endblock %} + + +{% block body %} + +{% block fos_user_content %} + + {# L'utilisateur est-il connecté ? #} + {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} + {# L'utilisateur est connecté ! #} + {% include "RegleBundle:Home:index.html.twig" %} + {% else %} + {# L'utilisateur n'est pas connecté ! #} +
+ +
+ +

+
+ + {% if attempt < 6 %} +
+

+ + + +

+ {% if error %} + + {% else %} + + {% endif %} + {{ 'login.connexion'|trans }} + +

{{ 'login.btn.pwd_forgot'|trans }}

+
+ {% else %} + +

{{ 'login.btn.pwd_forgot'|trans }}

+ {% endif %} +
+
+
+

+ {{ 'login.text_up_right'|trans }} + {{ 'login.btn.here'|trans }} +

+ {#

+ {{ 'login.text_bottom_right'|trans }} +

+

+ +

#} +
+ +
+
+ {% endif %} +{% endblock fos_user_content %} +{# ------------- PARAMETRES JQUERY ------------- #} + +{# ------------- PARAMETRES JQUERY ------------- #} + +{% endblock %} + +{% block cssin %} + body { + background: url({{ asset('bundles/login/images/') }}fd.jpg) repeat-y; + background-color: #9DC0E1; + overflow-y:hidden; + } + + #myd_connexion .logo { + background: url({{ asset('bundles/login/images/') }}logo.png) no-repeat; + background-size: 158px; + } + + .btn-myd, .btn-mydinv, .btn-mydinv:hover, .btn-myd:hover { + background: url({{ asset('bundles/login/images/') }}btn.png); + } + + #myd_connexion .load { + background: url({{ asset('bundles/login/images/') }}loadlogin.gif); + height: 16px; + width: 16px; + display: inline-block; + } + + .btn-myd { + background-position: left top; + } + + .btn-myd:hover { + background-position: left bottom; + } + + .btn-mydinv { + background-position: left bottom; + } + + .btn-mydinv:hover { + background-position: left top; + } +{% endblock cssin %} + +{% block css %} + +{% endblock css %} + \ No newline at end of file diff --git a/src/Myddleware/LoginBundle/Resources/views/Layout/style.css.twig b/src/Myddleware/LoginBundle/Resources/views/Layout/style.css.twig new file mode 100644 index 000000000..8040e3a9b --- /dev/null +++ b/src/Myddleware/LoginBundle/Resources/views/Layout/style.css.twig @@ -0,0 +1,103 @@ +{#/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ #} + +#reset_password, #change_password { + color: #0067AD; +} + +#change_password { + +} + +#change_password #fos_user_resetting_form { + label: inline-block; + width: 200px; +} + +#change_password #fos_user_resetting_form input { + width: 190px; +} + +/* boutons */ + +.btn-myd, .btn-mydinv, .btn-mydinv:hover, .btn-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn.png) no-repeat; + height: 31px; + width: 196px; + border: none; + display:inline-block; + cursor:pointer; +} + +.btn-small-myd, .btn-small-mydinv, .btn-small-mydinv:hover, .btn-small-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn-small.png) no-repeat; + height: 31px; + width: 50px; + border: none; + display:inline-block; +} + +.btn-middle-myd, .btn-myddle-mydinv, .btn-middle-mydinv:hover, .btn-middle-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn-middle.png) no-repeat; + height: 31px; + width: 100px; + border: none; + display:inline-block; +} + +.btn-large-myd, .btn-large-mydinv, .btn-large-mydinv:hover, .btn-large-myd:hover { + color: #404042; + font-weight: bold; + background: url({{ asset('bundles/regle/images/') }}template/btn-large.png) no-repeat; + height: 31px; + width: 278px; + border: none; + display:inline-block; + cursor:pointer; +} + + +.btn-myd, .btn-small-myd, .btn-middle-myd, .btn-large-myd { + background-position: left top; +} + +.btn-myd:hover, .btn-small-myd:hover, .btn-middle-myd:hover, .btn-large-myd:hover { + background-position: left bottom; + text-decoration:none; +} + +.btn-mydinv, .btn-small-mydinv, .btn-middle-mydinv, .btn-large-mydinv { + background-position: left bottom; +} + +.btn-mydinv:hover, .btn-small-mydinv:hover, .btn-middle-mydinv:hover, .btn-large-mydinv:hover { + background-position: left top; + text-decoration:none; +} diff --git a/src/Myddleware/RegleBundle/Classes/document.php b/src/Myddleware/RegleBundle/Classes/document.php new file mode 100644 index 000000000..7a5aea15d --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/document.php @@ -0,0 +1,1689 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +use Symfony\Bridge\Monolog\Logger; // Gestion des logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Accède aux services +use Doctrine\DBAL\Connection; // Connexion BDD +// use Myddleware\RegleBundle\Encryption\encryption as enCrypt; // Encryption +use Myddleware\RegleBundle\Classes\tools as MyddlewareTools; // SugarCRM Myddleware + +class documentcore { + + public $id; + + protected $typeError = 'S'; + protected $message = ''; + protected $dateCreated; + protected $connection; + protected $ruleName; + protected $ruleVersion; + protected $ruleMode; + protected $ruleId; + protected $ruleFields; + protected $fieldsType; + protected $ruleRelationships; + protected $ruleParams; + protected $sourceId; + protected $targetId; + protected $sourceData; + protected $data; + protected $type_document; + protected $jobActive = true; + protected $attempt; + protected $userId; + protected $status; + protected $document_data; + protected $solutionTarget; + protected $solutionSource; + protected $jobId; + protected $key; + protected $docIdRefError; + protected $tools; + protected $globalStatus = array( + 'New' => 'Open', + 'Predecessor_OK' => 'Open', + 'Relate_OK' => 'Open', + 'Transformed' => 'Open', + 'Ready_to_send' => 'Open', + 'Filter_OK' => 'Open', + 'Send' => 'Close', + 'Filter' => 'Cancel', + 'No_send' => 'Cancel', + 'Cancel' => 'Cancel', + 'Create_KO' => 'Cancel', + 'Filter_KO' => 'Error', + 'Predecessor_KO' => 'Error', + 'Relate_KO' => 'Error', + 'Error_transformed' => 'Error', + 'Error_history' => 'Error', + 'Error_sending' => 'Error' + ); + + protected $container; + protected $logger; + + static function lstGblStatus() { + return array( + 'Open' => 'flux.gbl_status.open', + 'Close' => 'flux.gbl_status.close', + 'Cancel' => 'flux.gbl_status.cancel', + 'Error' => 'flux.gbl_status.error' + ); + } + + static function lstStatus() { + return array( + 'New' => 'flux.status.new', + 'Predecessor_OK' => 'flux.status.predecessor_ok', + 'Relate_OK' => 'flux.status.relate_ok', + 'Transformed' => 'flux.status.transformed', + 'Ready_to_send' => 'flux.status.ready_to_send', + 'Filter_OK' => 'flux.status.filter_ok', + 'Send' => 'flux.status.send', + 'Filter' => 'flux.status.filter', + 'No_send' => 'flux.status.no_send', + 'Cancel' => 'flux.status.cancel', + 'Filter_KO' => 'flux.status.filter_ko', + 'Create_KO' => 'flux.status.create_ko', + 'Predecessor_KO' => 'flux.status.predecessor_ko', + 'Relate_KO' => 'flux.status.relate_ko', + 'Error_transformed' => 'flux.status.error_transformed', + 'Error_history' => 'flux.status.error_history', + 'Error_sending' => 'flux.status.error_sending' + ); + } + + // Instanciation de la classe de génération de log Symfony + public function __construct(Logger $logger, Container $container, Connection $dbalConnection, $param) { + $this->connection = $dbalConnection; + $this->logger = $logger; + $this->container = $container; + + // Chargement des solution si elles sont présentent dans les paramètres de construction + if (!empty($param['solutionTarget'])) { + $this->solutionTarget = $param['solutionTarget']; + } + if (!empty($param['solutionSource'])) { + $this->solutionSource = $param['solutionSource']; + } + if (!empty($param['jobId'])) { + $this->jobId = $param['jobId']; + } + if (!empty($param['key'])) { + $this->key = $param['key']; + } + if (!empty($param['fieldsType'])) { + $this->fieldsType = $param['fieldsType']; + } + + // Stop the processus if the job has been manually stopped + if ($this->getJobStatus() != 'Start') { + $this->jobActive = false; + } + + // Si le mode est Front office alors on n'initialise pas les attributs de l'objet document + if( + empty($param['mode']) + || ( + !empty($param['mode']) + && $param['mode'] != 'front_office' + ) + ) { + if (!empty($param['id_doc_myddleware'])) { + $this->setDocument($param['id_doc_myddleware']); + } + else { + $this->id = uniqid('', true); + $this->dateCreated = gmdate('Y-m-d H:i:s'); + $this->ruleName = $param['rule']['rule_name_slug']; + $this->ruleVersion = $param['rule']['rule_version']; + $this->ruleMode = $param['rule']['rule_mode']; + $this->ruleId = $param['rule']['rule_id']; + $this->ruleFields = $param['ruleFields']; + $this->data = $param['data']; + $this->sourceId = $this->data['id']; + $this->userId = $param['rule']['rule_created_by']; + $this->status = 'New'; + $this->attempt = 0; + } + // Ajout des paramètre de la règle + $this->setRuleParams(); + } + // Mise à jour des tableaux s'ils existent. + if (!empty($param['ruleFields'])) { + $this->ruleFields = $param['ruleFields']; + } + if (!empty($param['ruleRelationships'])) { + $this->ruleRelationships = $param['ruleRelationships']; + } + $this->tools = new MyddlewareTools($this->logger, $this->container, $this->connection); + } + + public function setDocument($id_doc) { + try { + $sqlParams = " SELECT + Documents.*, + Rule.rule_name_slug, + Rule.rule_version, + RuleParams.rulep_value rule_mode, + Rule.conn_id_source, + Rule.rule_module_source + FROM Documents + INNER JOIN Rule + ON Documents.rule_id = Rule.rule_id + INNER JOIN RuleParams + ON RuleParams.rule_id = Rule.rule_id + AND rulep_name= 'mode' + WHERE id = :id_doc"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":id_doc", $id_doc); + $stmt->execute(); + $this->document_data = $stmt->fetch(); + if (!empty($this->document_data['id'])) { + $this->id = $this->document_data['id']; + $this->dateCreated = $this->document_data['date_created']; + $this->userId = $this->document_data['created_by']; + $this->ruleId = $this->document_data['rule_id']; + $this->status = $this->document_data['status']; + $this->sourceId = $this->document_data['source_id']; + $this->targetId = $this->document_data['target_id']; + $this->ruleName = $this->document_data['rule_name_slug']; + $this->ruleVersion = $this->document_data['rule_version']; + $this->ruleMode = $this->document_data['rule_mode']; + $this->type_document = $this->document_data['type']; + $this->attempt = $this->document_data['attempt']; + } + else { + $this->logger->error( 'Failed to retrieve Document '.$id_doc.'.'); + } + } + catch (\Exception $e) { + $this->message .= 'Failed to retrieve document : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error($this->message); + $this->createDocLog(); + } + } + + public function createDocument() { + // On ne fait pas de beginTransaction ici car on veut pouvoir tracer ce qui a été fait ou non. Si le créate n'est pas du tout fait alors les données sont perdues + // L'enregistrement même partiel d'un document nous permet de tracer l'erreur. + try { + // Return false if job has been manually stopped + if (!$this->jobActive) { + $this->message .= 'Job is not active. '; + return false; + } + + // Création du header de la requête + $query_header = "INSERT INTO Documents (id, rule_id, date_created, date_modified, created_by, modified_by, source_id, target_id,source_date_modified, mode, type) VALUES"; + + // Récupération du type de document : vérification de l'existance d'un enregistrement avec le même ID dans Myddleware (passage du type docuement en U ou C) + $this->type_document = $this->checkRecordExist($this->sourceId); + + // SI la règle est en mode search alors on met le document en mode search aussi + if ($this->ruleMode == 'S') { + $old_type_document = $this->type_document; + $this->type_document = 'S'; + } + + // Création de la requête d'entête + $date_modified = $this->data['date_modified']; + // Encodage en UTF8, nécessaire pour SAP car les id peuvent avoir des accents + $query_header .= "('$this->id','$this->ruleId','$this->dateCreated','$this->dateCreated','$this->userId','$this->userId','$this->sourceId','$this->targetId','$date_modified','$this->ruleMode','$this->type_document')"; + $stmt = $this->connection->prepare($query_header); + $stmt->execute(); + + // Si la règle est seulement en création et que le document est en update alors on passe au document suivant + // Cependant si le document vient d'une règle child alors on autorise l'Update + if ( + $this->ruleMode == 'C' + && $this->type_document == 'U' + && ( + empty($this->ruleParams['group']) + || ( + !empty($this->ruleParams['group']) + && $this->ruleParams['group'] != 'child' + ) + ) + ) { + $this->message .= 'Rule mode only allows to create data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + $createDocuement = $this->updateSourceTable(); + } + // Si la règle est seulement en recherche et que le document est en update alors on passe au document suivant + elseif ( + $this->ruleMode == 'S' + && !empty($old_type_document) + && $old_type_document == 'U' + ) { + $this->message .= 'Rule mode only allows to search data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + $createDocuement = $this->updateSourceTable(); + } + elseif ( + $this->ruleMode == 'S' + && $this->type_document == 'U' + ) { + $this->message .= 'Rule mode only allows to search data. Filter because this document updates data.'; + $this->updateStatus('Filter'); + $createDocuement = $this->updateSourceTable(); + } + elseif (!empty($this->type_document)) { + // Mise à jour de la table des données source + $createDocuement = $this->updateSourceTable(); + if ($createDocuement) { + $this->updateStatus('New'); + } + else { + throw new \Exception( 'Failed to create source data for this document.' ); + } + } + else { + throw new \Exception( 'Failed to get the mode of the document. Failed to create the document.' ); + } + return $createDocuement; + } catch (\Exception $e) { + $this->message .= 'Failed to create document (id source : '.$this->id.'): '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->updateStatus('Create_KO'); + $this->logger->error($this->message); + return false; + } + } + + // Permet de filtrer ou non un document + public function filterDocument($ruleFilters) { + // Return false if job has been manually stopped + if (!$this->jobActive) { + $this->message .= 'Job is not active. '; + return false; + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + $filterOK = true; + // Si des filtres sont présents + if (!empty($ruleFilters)) { + // Récupération des données source + $this->getSourceData(); + + // Boucle sur les filtres + foreach ($ruleFilters as $ruleFilter) { + if(!$this->checkFilter($this->sourceData[$ruleFilter['rfi_target']],$ruleFilter['rfi_type'],$ruleFilter['rfi_value'])) { + $this->message .= 'This document is filtered. This operation is false : '.$ruleFilter['rfi_target'].' '.$ruleFilter['rfi_type'].' '.$ruleFilter['rfi_value'].'.'; + $this->updateStatus('Filter'); + $filterOK = -1; + break; + } + } + } + // Si on a pas eu d'erreur alors le document passe à l'étape suivante + if ($filterOK === true) { + $this->updateStatus('Filter_OK'); + } + $this->connection->commit(); // -- COMMIT TRANSACTION + return $filterOK; + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to filter document : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->updateStatus('Filter_KO'); + $this->logger->error($this->message); + return false; + } + } + + public function getJobStatus() { + $sqlJobDetail = "SELECT * FROM Job WHERE job_id = :jobId"; + $stmt = $this->connection->prepare($sqlJobDetail); + $stmt->bindValue(":jobId", $this->jobId); + $stmt->execute(); + $job = $stmt->fetch(); // 1 row + if (!empty($job['job_status'])) { + return $job['job_status']; + } + return false; + } + + public function getRuleId() { + return $this->ruleId; + } + + public function getMessage() { + return $this->message; + } + + public function getJobActive() { + return $this->jobActive; + } + + public function setMessage($message) { + $this->message .= $message; + } + + public function setTypeError($typeError) { + $this->typeError = $typeError; + } + + // Permet d'indiquer si le filtreest rempli ou pas + protected function checkFilter($fieldValue,$operator,$filterValue){ + switch ($operator) { + case 'content': + $pos = stripos($fieldValue, $filterValue); + if ($pos === false) { + return false; + } + else { + return true; + } + break; + case 'notcontent': + $pos = stripos($fieldValue, $filterValue); + if ($pos === false) { + return true; + } + else { + return false; + } + break; + case 'begin': + $begin = substr($fieldValue, 0, strlen($filterValue)); + if (strtoupper($begin) == strtoupper($filterValue)) { + return true; + } + else { + return false; + } + break; + case 'end': + $begin = substr($fieldValue, 0-strlen($filterValue)); + if (strtoupper($begin) == strtoupper($filterValue)) { + return true; + } + else { + return false; + } + break; + case 'in': + if (in_array(strtoupper($fieldValue),explode(';',strtoupper($filterValue)))) { + return true; + } + else { + return false; + } + break; + case 'notin': + if (!in_array(strtoupper($fieldValue),explode(';',strtoupper($filterValue)))) { + return true; + } + else { + return false; + } + break; + case 'gt': + if ($fieldValue > $filterValue) { + return true; + } + else { + return false; + } + break; + case 'lt': + if ($fieldValue < $filterValue) { + return true; + } + else { + return false; + } + break; + case 'lteq': + if ($fieldValue <= $filterValue) { + return true; + } + else { + return false; + } + break; + case 'gteq': + if ($fieldValue >= $filterValue) { + return true; + } + else { + return false; + } + break; + case 'is': + if (strtoupper($fieldValue) == strtoupper($filterValue)) { + return true; + } + else { + return false; + } + break; + case 'not': + if (strtoupper($fieldValue) != strtoupper($filterValue)) { + return true; + } + else { + return false; + } + break; + default: + $this->message .= 'Failed to filter. Operator '.$operator.' unknown. '; + return false; + } + + } + + // Permet de valider qu'aucun document précédent pour la même règle et le même id sont bloqués + public function ckeckPredecessorDocument() { + // Return false if job has been manually stopped + if (!$this->jobActive) { + $this->message .= 'Job is not active. '; + return false; + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + // Vérification que pour les documents en modification, les création n'ont pas de prédécesseur + if ($this->document_data['type'] == 'U') { + // Avant de vérifier les prédécesseur, on re vérifie que le document en cours est toujours du type UPDATE. + // En effet si un docuement précédent a été annulé il se peut que se docuement doivent être transformé en CREATE + // On ne fait cette vérification que si le target_id n'est pas renseigné, il peut l'etre quand on se sert de myddleware_element_id (plusieurs modules source pour un seul cible) + if (empty($this->targetId)) { + $this->type_document = $this->checkRecordExist($this->sourceId); + } + // Si le document doit réellement changé en CREATE, on modifie le type du document et on passe le document en Predecessor_OK + if($this->type_document == 'C') { + $this->updateType($this->type_document); + } + // Sinon on fait la recherche du prédécesseur classique + else { + // Selection des documents antérieurs de la même règle (toute version confondues) avec le même id au statut différent de closed et Cancel + $sqlParams = " SELECT + Documents.id + FROM Documents + INNER JOIN Rule + ON Documents.rule_id = Rule.rule_id + AND Rule.rule_name_slug = :rule_name_slug + INNER JOIN Rule Rule_version + ON Rule_version.rule_name = Rule.rule_name + WHERE + Rule_version.conn_id_source = :conn_id_source + AND Rule_version.rule_module_source = :rule_module_source + AND Documents.source_id = :source_id + AND Documents.global_status NOT IN ('Close','Cancel') + AND Documents.date_created < :date_created + LIMIT 1"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":rule_name_slug", $this->document_data['rule_name_slug']); + $stmt->bindValue(":conn_id_source", $this->document_data['conn_id_source']); + $stmt->bindValue(":source_id", $this->document_data['source_id']); + $stmt->bindValue(":rule_module_source", $this->document_data['rule_module_source']); + $stmt->bindValue(":date_created", $this->document_data['date_created']); + $stmt->execute(); + $result = $stmt->fetch(); + + // Si aucun prédécesseur en erreur sur la règle en cours on vérifie les document de la règle inverse (bidirectionnelle) si elle existe + if ( + empty($result['id']) + && !empty($this->ruleParams['bidirectional']) + ) { + $sqlParams = " SELECT + Documents.id + FROM Documents + INNER JOIN Rule + ON Documents.rule_id = Rule.rule_id + AND Rule.rule_id = :bidirectional + INNER JOIN Rule Rule_version + ON Rule_version.rule_name = Rule.rule_name + WHERE + Rule_version.conn_id_target = :conn_id_source + AND Rule_version.rule_module_target = :rule_module_source + AND Documents.target_id = :source_id + AND Documents.global_status NOT IN ('Close','Cancel') + AND Documents.date_created < :date_created + LIMIT 1"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":bidirectional", $this->ruleParams['bidirectional']); + $stmt->bindValue(":conn_id_source", $this->document_data['conn_id_source']); + $stmt->bindValue(":rule_module_source",$this->document_data['rule_module_source']); + $stmt->bindValue(":source_id",$this->document_data['source_id']); + $stmt->bindValue(":date_created",$this->document_data['date_created']); + $stmt->execute(); + $result = $stmt->fetch(); + } + + // Si un prédécesseur non clos est trouvé on passe le document au statut Predecessor_KO + if (!empty($result['id'])) { + $this->docIdRefError = $result['id']; + throw new \Exception('Another document for the same record is not close. This document is queued. '); + } + else { + // Mise à jour du target id si celui-ci n'était pas renseigné (document précédent sans target id au moment de la creation de ce docuement) + if (empty($this->targetId)) { + // Récupération de $this->targetId dans les documents clos + $this->checkRecordExist($this->document_data['source_id']); + if (!empty($this->targetId)) { + if(!$this->updateTargetId($this->targetId)) { + throw new \Exception('Failed to update the target id. Failed to unblock this update document. '); + } + } + // Si on est sur une règle groupée avec un child alors il est possible qu'il n'y ait pas de prédécesseur + // mais que l'on vienne quand même mettre à jour la règle root. + elseif ( + empty($this->ruleParams['group']) + || ( + !empty($this->ruleParams['group']) + && $this->ruleParams['group'] != 'child' + ) + ) { + throw new \Exception('Failed to retrieve the target in a parent document. Failed to unblock this update document. '); + } + } + } + } + } + + $this->updateStatus('Predecessor_OK'); + $this->connection->commit(); // -- COMMIT TRANSACTION + return true; + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to check document predecessor : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->updateStatus('Predecessor_KO'); + $this->logger->error($this->message); + return false; + } + } + + // Permet de valider qu'aucun document précédent pour la même règle et le même id sont bloqués + public function ckeckParentDocument($ruleRelationships) { + // Return false if job has been manually stopped + if (!$this->jobActive) { + $this->message .= 'Job is not active. '; + return false; + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + // S'il y a au moins une relation sur la règle et si on n'est pas sur une règle groupée + // alors on contôle les enregistrements parent + if ( + !empty($ruleRelationships) + && empty($this->ruleParams['group']) + ) { + $this->getSourceData(); + $error = false; + // Vérification de chaque relation de la règle + foreach ($ruleRelationships as $ruleRelationship) { + if(empty(trim($this->sourceData[$ruleRelationship['rrs_field_name_source']]))) { + continue; // S'il n'y a pas de relation, on envoie sans erreur + } + // Selection des documents antérieurs de la même règle avec le même id au statut différent de closed + $targetId = $this->getTargetId($ruleRelationship,$this->sourceData[$ruleRelationship['rrs_field_name_source']]); + if (empty($targetId)) { + $error = true; + break; + } + } + // Si aucun document parent n'est trouvé alors bloque le document + if ($error) { + // récupération du nom de la règle pour avoir un message plus clair + $sqlParams = " SELECT rule_name FROM Rule WHERE rule_id = :rule_id"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":rule_id", $ruleRelationship['rrs_field_id']); + $stmt->execute(); + $ruleResult = $stmt->fetch(); + $direction = $this->getRelationshipDirection($ruleRelationship); + throw new \Exception( 'Failed to retrieve a related document. No data for the field '.$ruleRelationship['rrs_field_name_source'].'. There is not record with the '.($direction == '-1' ? 'source_id' : 'target_id').' '.$this->sourceData[$ruleRelationship['rrs_field_name_source']].' in the rule '.$ruleResult['rule_name'].'. This document is queued. '); + } + } + $this->updateStatus('Relate_OK'); + $this->connection->commit(); // -- COMMIT TRANSACTION + return true; + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'No data for the field '.$ruleRelationship['rrs_field_name_source'].' in the rule '.$this->ruleName.'. Failed to check document related : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->updateStatus('Relate_KO'); + $this->logger->error($this->message); + return false; + } + } + + // Permet de transformer les données source en données cibles + public function transformDocument() { + // Return false if job has been manually stopped + if (!$this->jobActive) { + $this->message .= 'Job is not active. '; + return false; + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + // Transformation des données et insertion dans la table target + $transformed = $this->updateTargetTable(); + if ($transformed) { + // If the type of this document is Update and the id of the target is missing, we try to get this ID + if ( + $this->type_document == 'U' + && empty($this->targetId) + ) { + $this->checkRecordExist($this->document_data['source_id']); + if (!empty($this->targetId)) { + if(!$this->updateTargetId($this->targetId)) { + throw new \Exception( 'The type of this document is Update. Failed to update the target id '.$this->targetId.' on this document. This document is queued. ' ); + } + } + else { + throw new \Exception( 'The type of this document is Update. The id of the target is missing. This document is queued. ' ); + } + } + } + else { + throw new \Exception( 'Failed to transformed data. This document is queued. ' ); + } + $this->updateStatus('Transformed'); + $this->connection->commit(); // -- COMMIT TRANSACTION + return true; + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to transform document : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->updateStatus('Error_transformed'); + $this->logger->error($this->message); + return false; + } + } + + // Permet de transformer les données source en données cibles + public function getTargetDataDocument() { + // Return false if job has been manually stopped + if (!$this->jobActive) { + $this->message .= 'Job is not active. '; + return false; + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + + // Si le document est une modification de données alors on va chercher les données dans la cible avec l'ID + if ($this->type_document == 'U') { + // Récupération des données avec l'id de la cible + $searchFields = array('id' => $this->targetId); + $history = $this->getDocumentHistory($searchFields); + + // Ici la table d'historique doit obligatoirement avoir été mise à jour pour continuer + if ($history !== -1) { + $this->updateStatus('Ready_to_send'); + $return = true; + } + else { + $this->message .= 'Failed to retrieve record in target system before update. Id target : '.$this->targetId.'. Check this record is not deleted.'; + $this->typeError = 'E'; + $this->updateStatus('Error_history'); + $return = false; + } + } + // Si on est en mode recherche on récupère la donnée cible avec les paramètre de la source + elseif ($this->type_document == 'S') { + $this->getSourceData(); + if (!empty($this->sourceData)) { + // Un seul champ de recherche pour l'instant. Les règle recherche ne peuvent donc n'avoir qu'un seul champ + $searchFields[$this->ruleField[0]['rulef_target_field_name']] = $this->getTransformValue($this->sourceData,$this->ruleFields[0]); + } + else { + $this->message .= 'Failed to search data because there is no field in the query. This document is queued. '; + $this->typeError = 'E'; + $this->updateStatus('Error_history'); + $return = false; + } + + if(!empty($searchFields)) { + $history = $this->getDocumentHistory($searchFields); + } + else { + $history = -1; + } + + // Gestion de l'erreur + if ($history === -1) { + $this->message .= 'Failed to search data because the query is empty. This document is queued. '; + $this->typeError = 'E'; + $this->updateStatus('Error_history'); + $return = false; + } + // Si la fonction renvoie false (pas de données trouvée dans la cible) ou true (données trouvée et correctement mise à jour) + elseif ($history === false) { + $rule = $this->getRule(); + $this->message .= 'No data found in the target application. To synchronize data, you have to create a record in the target module ('.$rule['rule_module_target'].') with these data : '.print_r($searchFields,true).'. Then rerun this document. This document is queued. '; + $this->typeError = 'E'; + $this->updateStatus('Error_history'); + $return = false; + } + // renvoie l'id : Si une donnée est trouvée dans le système cible alors on passe le flux à envoyé car le lien est fait + else { + $this->updateStatus('Send'); + $this->updateTargetId($history); + $return = true; + } + } + // Si on est en création et que la règle a un paramètre de recherche de doublon, on va chercher dans la cible + elseif (!empty($this->ruleParams['duplicate_fields'])) { + $duplicate_fields = explode(';',$this->ruleParams['duplicate_fields']); + // Charge les données source du document dans $this->sourceData + $this->getSourceData(); + // Récupération des valeurs de la source pour chaque champ de recherche + foreach($duplicate_fields as $duplicate_field) { + foreach ($this->ruleFields as $ruleField) { + if($ruleField['rulef_target_field_name'] == $duplicate_field) + $sourceDuplicateField = $ruleField['rulef_source_field_name']; + } + // On ne fait pas de recherche dans la cible sur des champs vides. S'ils sont vides, ils sont excluent. + if (!empty($sourceDuplicateField)) { + $searchFields[$duplicate_field] = $this->sourceData[$sourceDuplicateField]; + } + } + if(!empty($searchFields)) { + $history = $this->getDocumentHistory($searchFields); + } + else { + $history = -1; + } + if ($history === -1) { + $this->message .= 'Failed to search duplicate data in the target system. This document is queued. '; + $this->typeError = 'E'; + $this->updateStatus('Error_history'); + $return = false; + } + // Si la fonction renvoie false (pas de données trouvée dans la cible) ou true (données trouvée et correctement mise à jour) + elseif ($history === false) { + $this->updateStatus('Ready_to_send'); + $return = true; + } + // renvoie l'id : Si une donnée est trouvée dans le système cible alors on modifie le document pour ajouter l'id target et modifier le type + else { + $this->updateStatus('Ready_to_send'); + $this->updateTargetId($history); + $this->updateType('U'); + $return = true; + } + } + // Sinon on mets directement le document en ready to send + else { + $this->updateStatus('Ready_to_send'); + $return = true; + } + + // S'il n'y a aucun changement entre la cible actuelle et les données qui seront envoyée alors on clos directement le document + // Si le document est en type recherche, alors la sible est forcément égal à la source et il ne fait pas annuler le doc. + if ($this->type_document != 'S') { + $this->checkNoChange(); + } + $this->connection->commit(); // -- COMMIT TRANSACTION + return $return; + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to get target document : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error($this->message); + $this->createDocLog(); + return false; + } + } + + // Vérifie si les données sont différente entre ce qu'il y a dans la cible et ce qui devrait être envoyé + protected function checkNoChange() { + // Récupération des données à envoyer + $tableTarget = "z_".$this->ruleName."_".$this->ruleVersion."_target"; + $sendQuery = "SELECT * FROM $tableTarget WHERE id_".$this->ruleName."_".$this->ruleVersion."_target = '$this->id'"; + $stmt = $this->connection->prepare($sendQuery); + $stmt->execute(); + $send = $stmt->fetch(); + + // Récupération des données actuele de la cible + $tableHistory = "z_".$this->ruleName."_".$this->ruleVersion."_history"; + $currentQuery = "SELECT * FROM $tableHistory WHERE id_".$this->ruleName."_".$this->ruleVersion."_history = '$this->id'"; + $stmt = $this->connection->prepare($currentQuery); + $stmt->execute(); + $history = $stmt->fetch(); + + // Comparaison des tableau + if ( + !empty($history) + && !empty($send) + ) { + $diff1 = array_diff($history, $send); + $diff2 = array_diff($send, $history); + // Si aucun changement alors clôture directe du document sans envoi à la cible + if ( + empty($diff1) + && empty($diff2) + ) { + $this->message .= 'Identical data to the target system. This document is canceled. '; + $this->typeError = 'W'; + $this->updateStatus('No_send'); + } + } + } + + // Récupération des données dans la cible et sauvegarde dans la table d'historique + protected function getDocumentHistory ($searchFields) { + // Permet de renseigner le tableau rule avec les données d'entête + $rule = $this->getRule(); + $read['module'] = $rule['rule_module_target']; + $read['fields'] = $this->getTargetFields(); + $read['query'] = $searchFields; + $read['ruleParams'] = $this->ruleParams; + $read['rule'] = $rule; + $dataTarget = $this->solutionTarget->read_last($read); + if (empty($dataTarget['done'])) { + return false; + } + elseif ($dataTarget['done'] === -1) { + $this->message .= $dataTarget['error']; + return -1; + } + else { + $updateHistory = $this->updateHistoryTable($dataTarget['values']); + if ($updateHistory === true) { + return $dataTarget['values']['id']; + } + // Erreur dans la mise à jour de la table historique + else { + $this->message .= $dataTarget['error']; + return -1; + } + } + } + + // Permet de charger les données du système source pour ce document + protected function getSourceData() { + try { + $table = "z_".$this->ruleName."_".$this->ruleVersion."_source"; + $tableId = "id_".$this->ruleName."_".$this->ruleVersion."_source"; + + // Récupération de toutes les sources au statut "New" pour la règle + $sqlSource = "SELECT $table.* + FROM Documents + INNER JOIN $table + ON Documents.id = $table.$tableId + WHERE + Documents.id = :id + "; + /*statut Predecessor_OK à revoir, sera certainement relate_OK*/ + $stmt = $this->connection->prepare($sqlSource); + $stmt->bindValue(":id", $this->id); + $stmt->execute(); + $sourceData = $stmt->fetch(); + $this->sourceData = $sourceData; + } catch (\Exception $e) { + $this->message .= 'Error getSourceData : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + } + } + + + // Mise à jour de la table des données source + protected function updateSourceTable() { + try { + $query_data = "INSERT INTO z_".$this->ruleName."_".$this->ruleVersion."_source VALUES "; + // Création de la requête de données + $query_data .= "("; + $columns_data = "DESCRIBE z_".$this->ruleName."_".$this->ruleVersion."_source"; + $stmt = $this->connection->prepare($columns_data); + $stmt->execute(); + $columns = $stmt->fetchAll(); + + $first = true; + if($columns) { + foreach ($columns as $column) { + if ($first === true) { + $query_data .= "'$this->id',"; + $first = false; + continue; + } + + // Si le champ Myddleware_element_id est trouve (champ relation sur l'id de la source en cours de lecture) + if ($column['Field'] == 'Myddleware_element_id') { + $value = $this->data['id']; + } + elseif (!empty($this->data[$column['Field']])) { + $value = $this->data[$column['Field']]; + } + else { + $value = ''; + } + $query_data .= "'".addslashes($value)."',"; + } + // Suppression de la dernière virgule + $query_data = rtrim($query_data,','); + $query_data .= ")"; + $stmt = $this->connection->prepare($query_data); + $stmt->execute(); + } + } + catch (\Exception $e) { + $this->message .= 'Failed : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + return false; + } + return true; + } + + + // Mise à jour de la table des données source + protected function updateHistoryTable($dataTarget) { + try { + $query_data = "INSERT INTO z_".$this->ruleName."_".$this->ruleVersion."_history VALUES "; + // Création de la requête de données + $query_data .= "("; + $columns_data = "DESCRIBE z_".$this->ruleName."_".$this->ruleVersion."_history"; + $stmt = $this->connection->prepare($columns_data); + $stmt->execute(); + $columns = $stmt->fetchAll(); + + $first = true; + if($columns) { + foreach ($columns as $column) { + + if ($first === true) { + $query_data .= "'$this->id',"; + $first = false; + } + // Si le champ Myddleware_element_id est demandé, on le renseigne avec le target_id + elseif($column['Field'] == 'Myddleware_element_id') { + $value = $dataTarget['id']; + $query_data .= "'".addslashes($value)."',"; + } + else { + if(!empty($dataTarget[$column['Field']])){ + $value = $dataTarget[$column['Field']]; + $query_data .= "'".addslashes($value)."',"; + } + // Si le champ est vide o ne décrypte pas, on laisse le champ vide + else { + $query_data .= "'',"; + } + } + } + + // Suppression de la dernière virgule + $query_data = rtrim($query_data,','); + $query_data .= ")"; + $stmt = $this->connection->prepare($query_data); + $stmt->execute(); + } + } catch (\Exception $e) { + $this->message .= 'Failed : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + return -1; + } + return true; + } + + // Mise à jour de la table des données cibles + protected function updateTargetTable() { + $nameIdSource = "id_".$this->ruleName."_".$this->ruleVersion."_source"; + // Charge les données source du document dans $this->sourceData + $this->getSourceData(); + if (!empty($this->sourceData)) { + try{ + // Préparation de la requête d'insertion dans la table target + $query_data = "INSERT INTO z_".$this->ruleName."_".$this->ruleVersion."_target VALUES "; + $query_data .= "("; + + // Récupération de tous les champs de la table target + $columns_data = "DESCRIBE z_".$this->ruleName."_".$this->ruleVersion."_target"; + $stmt = $this->connection->prepare($columns_data); + $stmt->execute(); + $columns = $stmt->fetchAll(); + + $first = true; + if($columns) { + // Boucle sur tous les champs target + foreach ($columns as $column) { + if ($first === true) { + $query_data .= "'".$this->sourceData[$nameIdSource]."',"; + $first = false; + } + else { + // Recherche du champ cible ($column['Field']) dans les valeurs de la source ($this->RuleFields) pour le transformer et le sauvegarder dans la table target + // Boucle sur toutes les champs jusqu'à trouver celle du champ en cours + if (!empty($this->ruleFields)) { + foreach ($this->ruleFields as $ruleField) { + // Si la formule est trouvée + if ($ruleField['rulef_target_field_name'] == $column['Field']) { + // Transformation du champ + $value = $this->getTransformValue($this->sourceData,$ruleField); + if ($value === false) { + throw new \Exception( 'Failed to transform data.' ); + } + $query_data .= "'".addslashes($value)."',"; + break; + } + } + } + if(isset($this->ruleRelationships)) { + // Récupération de l'ID target + foreach ($this->ruleRelationships as $ruleRelationships) { + // Si la formule est trouvée + if ($ruleRelationships['rrs_field_name_target'] == $column['Field']) { + // Transformation du champ + $value = $this->getTransformValue($this->sourceData,$ruleRelationships); + if ($value === false) { + throw new \Exception( 'Failed to transform relationship data.' ); + } + $query_data .= "'".addslashes($value)."',"; + break; + } + } + } + } + } + // Suppression de la dernière virgule + $query_data = rtrim($query_data,','); + $query_data .= ")"; + $stmt = $this->connection->prepare($query_data); + $stmt->execute(); + return true; + } + } + catch(Exception $e) { + $this->message .= 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + } + } + return false; + } + + + + /* + Fonction permettant de renvoyer la valeur du champ cible en fonction des données sources et de la règle (relation, formule) + En entrée, 2 tableaux sont attendus + => Les données source $source, par exemple : + Array + ( + [id_test_account_001_source] => 5352886318db0 : champ non nécessaire en mode test sur le front office + [name] => test sfaure 003 + [email1] => 003@test.test + ) + => La définition du champ cible (avec formule et relation) $RuleField, par exemple : + Array + ( + [rulef_id] => 52 : champ non nécessaire en mode test sur le front office + [rule_id] => 53500e0bf2d06 : champ non nécessaire en mode test sur le front office + [rulef_target_field_name] => name + [rulef_source_field_name] => name + [rulef_formula] => + [rulef_related_rule] => + ) + En sortie la fonction renvoie la valeur du champ à envoyer dans le cible + */ + public function getTransformValue($source,$ruleField) { + try { + //-- + if (!empty($ruleField['rulef_formula'])) { + // -- -- -- Gestion des formules + + // préparation des variables + $r = explode(';', $ruleField['rulef_source_field_name']); + if(count($r) > 1) { + foreach ( $r as $listFields ) { + // On ne traite pas l'entrée my_value + if ($listFields != 'my_value') { + $fieldNameDyn = $listFields; // value : nom de la variable exemple name + if (isset($source[$listFields])) { + $$fieldNameDyn = $source[$listFields]; // variable dynamique name = $name + } + else { + // Erreur + throw new \Exception( 'The field '.$listFields.' is unknow in the formula '.$ruleField['rulef_formula'].'. ' ); + } + } + } + } + else { + // On ne traite pas l'entrée my_value + if ($ruleField['rulef_source_field_name'] != 'my_value') { + $fieldNameDyn = $ruleField['rulef_source_field_name']; // value : nom de la variable exemple name + $$fieldNameDyn = $source[$ruleField['rulef_source_field_name']]; // variable dynamique name = $name + } + } + // préparation des variables + + $formule = $this->container->get('formula.myddleware'); // service formule myddleware + $formule->init($ruleField['rulef_formula']); // mise en place de la règle dans la classe + $formule->generateFormule(); // Genère la nouvelle formule à la forme PhP + + // Exécute la règle si pas d'erreur de syntaxe + if($f = $formule->execFormule()) { + eval('$rFormula = '.$f.';'); // exec + if(isset($rFormula)) { + // affectation du résultat + return $rFormula; + } + else { + throw new \Exception( 'Invalid formula (failed to retrieve formula) : '.$ruleField['rulef_formula'] ); + } + } + else { + throw new \Exception( 'Invalid formula (failed to execute) : '.$ruleField['rulef_formula'] ); + } + // -- -- -- Gestion des formules + } + // S'il s'agit d'un champ relation + elseif (!empty($ruleField['rrs_field_id'])) { + // Si l'id est vide on renvoie vide + if(empty(trim($source[$ruleField['rrs_field_name_source']]))){ + return null; + } + + // Si on est sur une règle groupée alors l'id à retourner est forcément Myddleware_element_id (seule relation possible pour les règles groupées) + if (!empty($this->ruleParams['group'])) { + if (!empty($source['Myddleware_element_id'])) { + return $source['Myddleware_element_id']; + } + else { + throw new \Exception( 'Failed to get the field Myddleware_element_id for the group rule. ' ); + } + } + + // Récupération de l'ID de l'enregistrement lié dans la cible avec l'id correspondant dans la source et la correspondance existante dans la règle liée. + $targetId = $this->getTargetId($ruleField,$source[$ruleField['rrs_field_name_source']]); + if (!empty($targetId)) { + return $targetId; + } + else { + throw new \Exception( 'Target id not found for id source '.$source[$ruleField['rulef_source_field_name']].' of the rule '.$ruleField['rulef_related_rule'] ); + } + } + // Si le champ est envoyé sans transformation + elseif (isset($source[$ruleField['rulef_source_field_name']])) { + return $this->checkField($source[$ruleField['rulef_source_field_name']]); + } + elseif (is_null($source[$ruleField['rulef_source_field_name']])) { + return null; + } + else { + throw new \Exception( 'Field '.$ruleField['rulef_source_field_name'].' not found in source data.------'.print_r($ruleField,true) ); + } + } + catch(\Exception $e) { + $this->typeError = 'E'; + $this->message .= 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error( $this->message ); + return false; + } + + } + + // Fonction permettant de contrôle les données. + protected function checkField($value) { + if (!empty($value)) { + return $value; + } + return null; + } + + // Permet de récupérer les données d'entête de la règle + protected function getRule() { + try { + if (!empty($this->ruleId)) { + $rule = "SELECT * FROM Rule WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($rule); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + return $stmt->fetch(); + } + } catch (\Exception $e) { + $this->typeError = 'E'; + $this->message .= 'Error getRule : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error( $this->message ); + } + } + + // Permet de récupérer les champs de la cible + protected function getTargetFields() { + try { + if (!empty($this->ruleId)) { + $rule = "SELECT * FROM RuleFields WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($rule); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $ruleFields = $stmt->fetchAll(); + foreach ($ruleFields AS $ruleField) { + $fields[] = $ruleField['rulef_target_field_name']; + } + + // Ajout des champs de relation s'il y en a + $rule = "SELECT * FROM RuleRelationShips WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($rule); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $ruleRelationShips = $stmt->fetchAll(); + if(!empty($ruleRelationShips)){ + foreach ($ruleRelationShips AS $ruleRelationShip) { + $fields[] = $ruleRelationShip['rrs_field_name_target']; + } + } + + return $fields; + } + } catch (\Exception $e) { + $this->typeError = 'E'; + $this->message .= 'Error getTargetFields : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error( $this->message ); + } + } + + // Permet de charger tous les paramètres de la règle + protected function setRuleParams() { + try { + $sqlParams = "SELECT * + FROM RuleParams + WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $ruleParams = $stmt->fetchAll(); + if($ruleParams) { + foreach ($ruleParams as $ruleParam) { + $this->ruleParams[$ruleParam['rulep_name']] = ltrim($ruleParam['rulep_value']); + } + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + // Permet de déterminer le type de document (Create ou Update) + // En entrée : l'id de l'enregistrement source + // En sortie : le type de docuement (C ou U) + protected function checkRecordExist($id) { + try { + // Si on est sur une règle de type groupe avec la valeur child alors on est focément en update (seule la règle root est autorisée à créer des données) + if ( + !empty($this->ruleParams['group']) + && $this->ruleParams['group'] == 'child' + ){ + return 'U'; + } + + // Recherche d'un enregitsrement avec un target id sur la même source quelques soit la version de la règle + // Le tri sur target_id permet de récupérer le target id non vide en premier + $sqlParams = " SELECT + Documents.id, + Documents.target_id + FROM Rule + INNER JOIN Rule Rule_version + ON Rule_version.rule_name = Rule.rule_name + INNER JOIN Documents + ON Documents.rule_id = Rule_version.rule_id + WHERE + Rule.rule_id IN (:ruleId) + AND Documents.global_status != 'Cancel' + AND Documents.source_id = :id + AND Documents.id != :id_doc + ORDER BY target_id DESC + LIMIT 1"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->bindValue(":id", $id); + $stmt->bindValue(":id_doc", $this->id); + $stmt->execute(); + $result = $stmt->fetch(); + + // Si on n'a pas trouvé de résultat et que la règle à une équivalente inverse (règle bidirectionnelle) + // Alors on recherche dans la règle opposée + if ( + empty($result['id']) + && !empty($this->ruleParams['bidirectional']) + ) { + $sqlParams = " SELECT + Documents.id, + Documents.source_id target_id + FROM Rule + INNER JOIN Rule Rule_version + ON Rule_version.rule_name = Rule.rule_name + INNER JOIN Documents + ON Documents.rule_id = Rule_version.rule_id + WHERE + Rule.rule_id IN (:ruleId) + AND Documents.global_status != 'Cancel' + AND Documents.target_id = :id + AND Documents.id != :id_doc + ORDER BY target_id DESC + LIMIT 1"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":ruleId", $this->ruleParams['bidirectional']); + $stmt->bindValue(":id", $id); + $stmt->bindValue(":id_doc", $this->id); + $stmt->execute(); + $result = $stmt->fetch(); + } + + if (!empty($result['id'])) { + $this->targetId = $result['target_id']; + return 'U'; + } + // Si aucun doc trouvé sur la règle actuelle + else { + // Si une relation avec le champ Myddleware_element_id est présente alors on passe en update et on change l'id source en prenant l'id de la relation + // En effet ce champ indique que l'on va modifié un enregistrement créé par une autre règle + if (!empty($this->ruleRelationships)) { + // Boucle sur les relation + foreach ($this->ruleRelationships as $ruleRelationship) { + // Si on est sur une relation avec le champ Myddleware_element_id + if ($ruleRelationship['rrs_field_name_target'] == 'Myddleware_element_id'){ + // Si le champs avec l'id source n'est pas vide + // S'il s'agit de Myddleware_element_id on teste id + if ( + !empty($this->data[$ruleRelationship['rrs_field_name_source']]) + || ( + $ruleRelationship['rrs_field_name_source'] == 'Myddleware_element_id' + && !empty($this->data['id']) + ) + ) { + // On recherche l'id target dans la règle liée + $this->sourceId = ($ruleRelationship['rrs_field_name_source'] == 'Myddleware_element_id' ? $this->data['id'] : $this->data[$ruleRelationship['rrs_field_name_source']]); + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":ruleId", $ruleRelationship['rrs_field_id']); + $stmt->bindValue(":id", $this->sourceId); + $stmt->bindValue(":id_doc", $this->id); + $stmt->execute(); + $result = $stmt->fetch(); + // Si on trouve la target dans la règle liée alors on passe le doc en UPDATE + if (!empty($result['id'])) { + $this->targetId = $result['target_id']; + return 'U'; + } + // Sinon on bloque la création du document + else { + throw new \Exception( 'Failed to get the id target of the current module in the rule linked.' ); + } + } + else { + throw new \Exception( 'Failed to get the id source of the current module.' ); + } + } + } + } + // Si aucune règle avec relation Myddleware_element_id alors on est en création + return 'C'; + } + } catch (\Exception $e) { + $this->typeError = 'E'; + $this->message .= 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error( $this->message ); + return null; + } + } + + public function updateStatus($new_status) { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + // On ajoute un contôle dans le cas on voudrait changer le statut + $new_status = $this->beforeStatusChange($new_status); + + $now = gmdate('Y-m-d H:i:s'); + // Récupération du statut global + $globalStatus = $this->globalStatus[$new_status]; + // Ajout d'un essai si erreur + if ($globalStatus == 'Error' || $globalStatus == 'Close') { + $this->attempt++; + } + $query = " UPDATE Documents + SET + date_modified = :now, + global_status = :globalStatus, + attempt = :attempt, + status = :new_status + WHERE + id = :id + "; + echo 'statut '.$new_status.' id = '.$this->id.' '.$now.chr(10);; + // Suppression de la dernière virgule + $stmt = $this->connection->prepare($query); + $stmt->bindValue(":now", $now); + $stmt->bindValue(":globalStatus", $globalStatus); + $stmt->bindValue(":attempt", $this->attempt); + $stmt->bindValue(":new_status", $new_status); + $stmt->bindValue(":id", $this->id); + $stmt->execute(); + $this->message .= 'Status : '.$new_status; + $this->connection->commit(); // -- COMMIT TRANSACTION + $this->status = $new_status; + $this->afterStatusChange($new_status); + $this->createDocLog(); + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Error status update : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + $this->createDocLog(); + } + } + + // Permet d'intervenir avant le changement de statut + protected function beforeStatusChange($new_status) { + return $new_status; + } + + // Permet d'intervenir après le changement de statut + protected function afterStatusChange($new_status) { + } + + // Permet de modifier le type du document + public function updateType($new_type) { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + $now = gmdate('Y-m-d H:i:s'); + $query = " UPDATE Documents + SET + date_modified = :now, + type = :new_type + WHERE + id = :id + "; + // Suppression de la dernière virgule + $stmt = $this->connection->prepare($query); + $stmt->bindValue(":now", $now); + $stmt->bindValue(":new_type", $new_type); + $stmt->bindValue(":id", $this->id); + $stmt->execute(); + $this->message .= 'Type : '.$new_type; + $this->connection->commit(); // -- COMMIT TRANSACTION + $this->createDocLog(); + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Error type : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + $this->createDocLog(); + } + } + + // Permet de modifier le type du document + public function updateTargetId($target_id) { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + $now = gmdate('Y-m-d H:i:s'); + $query = " UPDATE Documents + SET + date_modified = :now, + target_id = :target_id + WHERE + id = :id + "; + // Suppression de la dernière virgule + $stmt = $this->connection->prepare($query); + $stmt->bindValue(":now", $now); + $stmt->bindValue(":target_id", $target_id); + $stmt->bindValue(":id", $this->id); + $stmt->execute(); + $this->message .= 'Target id : '.$target_id; + $this->connection->commit(); // -- COMMIT TRANSACTION + $this->createDocLog(); + return true; + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Error target id : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + $this->createDocLog(); + return false; + } + } + + protected function getRelationshipDirection($ruleRelationship) { + try { + // Calcul du sens de la relation. Si on ne trouve pas (exemple des relations custom) alors on met 1 par défaut. + // depuis la version 1.1.2 on enlève les test sur delete des règles car un document en erreur peut porter sur une ancienne version de règle + $sqlParams = " SELECT + IF(RuleA.conn_id_source = RuleB.conn_id_source, '1', IF(RuleA.conn_id_source = RuleB.conn_id_target, '-1', '1')) direction + FROM RuleRelationShips + INNER JOIN Rule RuleA + ON RuleRelationShips.rule_id = RuleA.rule_id + #AND RuleA.rule_deleted = 0 + INNER JOIN Rule RuleB + ON RuleRelationShips.rrs_field_id = RuleB.rule_id + #AND RuleB.rule_deleted = 0 + WHERE + RuleRelationShips.rrs_id = :rrs_id + "; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":rrs_id", $ruleRelationship['rrs_id']); + $stmt->execute(); + $result = $stmt->fetch(); + if (!empty($result['direction'])) { + return $result['direction']; + } + return null; + } catch (\Exception $e) { + return null; + } + } + + // Permet de récupérer l'id target pour une règle (indépendemment de la version de la règle) et un id source + protected function getTargetId($ruleRelationship,$record_id) { + try { + $direction = $this->getRelationshipDirection($ruleRelationship); + // En fonction du sens de la relation, la recherche du parent id peut-être inversée (recherchée en source ou en cible) + if ($direction == '-1') { + $sqlParams = " SELECT source_id record_id + FROM Rule + INNER JOIN Rule Rule_version + ON Rule_version.rule_name = Rule.rule_name + INNER JOIN Documents + ON Documents.rule_id = Rule_version.rule_id + WHERE + Rule.rule_id = :ruleRelateId + AND Documents.source_id != '' + AND Documents.target_id = :record_id + AND Documents.global_status = 'Close' + LIMIT 1"; + } + elseif ($direction == '1') { + $sqlParams = " SELECT target_id record_id + FROM Rule + INNER JOIN Rule Rule_version + ON Rule_version.rule_name = Rule.rule_name + INNER JOIN Documents + ON Documents.rule_id = Rule_version.rule_id + WHERE + Rule.rule_id = :ruleRelateId + AND Documents.source_id = :record_id + AND Documents.target_id != '' + AND Documents.global_status = 'Close' + LIMIT 1"; + } + else { + throw new \Exception( 'Failed to find the direction of the relationship with the rule_id '.$ruleRelationship['rrs_field_id'].'. ' ); + } + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":ruleRelateId", $ruleRelationship['rrs_field_id']); + $stmt->bindValue(":record_id", $record_id); + $stmt->execute(); + $result = $stmt->fetch(); + if (!empty($result['record_id'])) { + return $result['record_id']; + } + return null; + } catch (\Exception $e) { + $this->message .= 'Error getTargetId : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->typeError = 'E'; + $this->logger->error( $this->message ); + } + } + + // Permet de renvoyer le statut du document + public function getStatus() { + return $this->status; + } + + // Fonction permettant de créer un log pour un docuement + // Les id de la soluton, de la règle et du document + // $type peut contenir : I (info;), W(warning), E(erreur), S(succès) + // $code contient le code de l'erreur + // $message contient le message de l'erreur avec potentiellement des variable &1, &2... + // $data contient les varables du message de type array('id_contact', 'nom_contact') + protected function createDocLog() { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + $now = gmdate('Y-m-d H:i:s'); + $this->message = substr(str_replace("'","",$this->message),0,1000); + $query_header = "INSERT INTO Log (log_created, log_type, log_msg, rule_id, doc_id, ref_doc_id, job_id) VALUES ('$now','$this->typeError','$this->message','$this->ruleId','$this->id','$this->docIdRefError','$this->jobId')"; + $stmt = $this->connection->prepare($query_header); + $stmt->execute(); + $this->message = ''; + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error( 'Failed to create log : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/document.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class document extends documentcore { + + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/home.php b/src/Myddleware/RegleBundle/Classes/home.php new file mode 100644 index 000000000..000e19dbc --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/home.php @@ -0,0 +1,239 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +use Symfony\Bridge\Monolog\Logger; // Gestion des logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Accède aux services +use Doctrine\DBAL\Connection; // Connexion BDD +use Myddleware\RegleBundle\Classes\tools as MyddlewareTools; + +class homecore { + + protected $connection; + protected $container; + protected $logger; + + protected $historicDays = 7; + protected $historicDateFormat = 'M-d'; + + + public function __construct(Logger $logger, Container $container, Connection $dbalConnection, $param = false) { + $this->logger = $logger; + $this->container = $container; + $this->connection = $dbalConnection; + } + + // + public function errorByRule($is_support, $id) { + try { + + if($is_support == false) { + $where = ' WHERE created_by='.$id; + } + else { + $where = ''; + } + + $sql = "SELECT + Rule.rule_name, + Rule.rule_version, + Rule.rule_id, + count(Documents.id) cpt + FROM Rule + LEFT OUTER JOIN Documents + ON Rule.rule_id = Documents.rule_id + AND Documents.global_status = 'Error' + $where + GROUP BY Rule.rule_name + HAVING cpt > 0 + ORDER BY cpt DESC"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetchAll(); + return $result; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + // + public function closedByRule($is_support, $id) { + + try { + + if($is_support == false) { + $where = 'AND created_by='.$id; + } + else { + $where = ''; + } + + $sql = "SELECT + Rule.rule_name, + count(Documents.id) cpt + FROM Rule + LEFT OUTER JOIN Documents + ON Rule.rule_id = Documents.rule_id + AND Documents.global_status = 'Close' + WHERE + Rule.rule_active = 1 + AND Rule.rule_deleted = 0 + $where + GROUP BY Rule.rule_name"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetchAll(); + return $result; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + public function listError($nbJour = false) { + try { + $sql = "SELECT + Rule.rule_name, + Documents.id, + Documents.date_modified + FROM Documents + INNER JOIN Rule + ON Rule.rule_id = Documents.rule_id + AND Documents.global_status = 'Error' + WHERE + Rule.rule_active = 1 + AND Rule.rule_deleted = 0"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetchAll(); + return $result; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + public function countTypeDoc($isAdmin, $id) { + try { + $where = ''; + if($isAdmin == false) { + $where = 'WHERE created_by='.$id; + } + + $sql = "SELECT count(*) as nb, global_status + FROM Documents + $where + GROUP BY global_status + ORDER BY nb DESC + "; + + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetchAll(); + + return $result; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + public function countTransferRule($isAdmin, $id) { + try { + $where = ''; + if($isAdmin == false) { + $where = ' AND created_by='.$id; + } + + $sql = "SELECT count(*) as nb, Rule.rule_name + FROM Rule + INNER JOIN Documents + ON Rule.rule_id = Documents.rule_id + WHERE + Documents.global_status = 'Close' + $where + GROUP BY rule_name + ORDER BY nb DESC + "; + + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetchAll(); + return $result; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + public function countTransferHisto($isAdmin, $id) { + try { + $historic = array(); + // Start date + $startDate = date('Y-m-d', strtotime( '-'.$this->historicDays.' days' ) );; + // End date + $endDate = date('Y-m-d'); + // Init array + while (strtotime($startDate) < strtotime($endDate)) { + // echo "$startDate\n "; + $startDateFromat = date ($this->historicDateFormat, strtotime('+1 day', strtotime($startDate))); + $startDate = date ("Y-m-d", strtotime('+1 day', strtotime($startDate))); + $historic[$startDate] = array('date' => $startDateFromat,'open' => 0,'error' => 0,'cancel' => 0,'close' => 0); + } + + $where = ''; + if($isAdmin == false) { + $where = ' AND created_by='.$id; + } + $sql = "SELECT DATE_FORMAT(date_modified, '%Y-%m-%d') date, Documents.* FROM Documents WHERE date_modified >= DATE_ADD(CURDATE(), INTERVAL -".$this->historicDays." DAY) ".$where; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetchAll(); + if (!empty($result)) { + foreach ($result as $row) { + if (isset($historic[$row['date']][strtolower($row['global_status'])])) { + $historic[$row['date']][strtolower($row['global_status'])]++; + } + } + } + return $historic; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + +} + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/home.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class home extends homecore { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/job.php b/src/Myddleware/RegleBundle/Classes/job.php new file mode 100644 index 000000000..a047ff61f --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/job.php @@ -0,0 +1,955 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +use Symfony\Bridge\Monolog\Logger; // Gestion des logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Accède aux services +use Doctrine\DBAL\Connection; // Connexion BDD +use Symfony\Component\HttpFoundation\Session\Session; +use Myddleware\RegleBundle\Classes\tools as MyddlewareTools; +use Symfony\Component\Filesystem\Filesystem; + +class jobcore { + + public $id; + public $message = ''; + public $createdJob = false; + + protected $container; + protected $connection; + protected $logger; + protected $tools; + + protected $rule; + protected $ruleId; + protected $limit = 100; + protected $logData; + protected $start; + protected $paramJob; + protected $manual; + protected $env; + protected $nbDayClearJob = 7; + + public function __construct(Logger $logger, Container $container, Connection $dbalConnection) { + $this->logger = $logger; // gestion des logs symfony monolog + $this->container = $container; + $this->connection = $dbalConnection; + $this->tools = new MyddlewareTools($this->logger, $this->container, $this->connection); + + $this->env = $this->container->getParameter("kernel.environment"); + $this->setManual(); + } + + /*Permet de charger toutes les données de la règle (en paramètre)*/ + public function setRule($rule_name_slug) { + try { + include_once 'rule.php'; + + // RECUPERE CONNECTEUR ID + $sqlRule = "SELECT * + FROM Rule + WHERE + rule_name_slug = :rule_name_slug + AND rule_deleted = 0 + "; + $stmt = $this->connection->prepare($sqlRule); + $stmt->bindValue("rule_name_slug", $rule_name_slug); + $stmt->execute(); + $rule = $stmt->fetch(); // 1 row + if (empty($rule['rule_id'])) { + throw new \Exception ('Rule '.$rule_name_slug.' doesn\'t exist or is deleted.'); + } + // Error if the rule is inactive and if we try to run it from a job (not manually) + elseif( + empty($rule['rule_active']) + && $this->manual == 0 + ) { + throw new \Exception ('Rule '.$rule_name_slug.' is inactive.'); + } + + $this->ruleId = $rule['rule_id']; + + // We instance the rule + $param['ruleId'] = $this->ruleId; + $param['jobId'] = $this->id; + $param['limit'] = $this->limit; + $param['manual'] = $this->manual; + $this->rule = new rule($this->logger, $this->container, $this->connection, $param); + return true; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $this->message .= $e->getMessage(); + return false; + } + } + + // Permet de contrôler si un docuement de la même règle pour le même enregistrement n'est pas close + public function createDocuments() { + if ($this->limit > 0) { + $createDocuments = $this->rule->createDocuments(); + if (!empty($createDocuments['error'])) { + $this->message .= print_r($createDocuments['error'],true); + } + if (!empty($createDocuments['count'])) { + $this->limit = $this->limit-$createDocuments['count']; + if ($this->limit < 0) { + $this->limit = 0; + } + return $createDocuments['count']; + } + else { + return 0; + } + } + else { + return 0; + } + } + + // Permet de contrôler si un docuement de la même règle pour le même enregistrement n'est pas close + public function ckeckPredecessorDocuments() { + $this->rule->ckeckPredecessorDocuments(); + } + + // Permet de filtrer les documents en fonction des filtres de la règle + public function filterDocuments() { + $this->rule->filterDocuments(); + } + + // Permet de contrôler si un docuement a une relation mais n'a pas de correspondance d'ID pour cette relation dans Myddleware + public function ckeckParentDocuments() { + $this->rule->ckeckParentDocuments(); + } + + // Permet de trasformer les documents + public function transformDocuments() { + $this->rule->transformDocuments(); + } + + // Permet de récupérer les données de la cible avant modification des données + // 2 cas de figure : + // - Le document est un document de modification + // - Le document est un document de création mais la règle a un paramètre de vérification des données pour ne pas créer de doublon + public function getTargetDataDocuments() { + $this->rule->getTargetDataDocuments(); + } + + // Ecriture dans le système source et mise à jour de la table document + public function sendDocuments() { + $sendDocuments = $this->rule->sendDocuments(); + if (!empty($sendDocuments['error'])) { + $this->message .= $sendDocuments['error']; + } + } + + // Ecriture dans le système source et mise à jour de la table document + public function runError($limit, $attempt) { + try { + // Récupération de tous les flux en erreur ou des flux en attente (new) qui ne sont pas sur règles actives (règle child pour des règles groupées) + $sqlParams = " SELECT * + FROM Documents + WHERE + ( + global_status = 'Error' + OR status = 'New' + ) + AND attempt <= :attempt + ORDER BY attempt ASC, source_date_modified ASC + LIMIT $limit"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue("attempt", $attempt); + $stmt->execute(); + $documentsError = $stmt->fetchAll(); + if(!empty($documentsError)) { + include_once 'rule.php'; + foreach ($documentsError as $documentError) { + $param['ruleId'] = $documentError['rule_id']; + $param['jobId'] = $this->id; + $rule = new rule($this->logger, $this->container, $this->connection, $param); + $errorActionDocument = $rule->actionDocument($documentError['id'],'rerun'); + if (!empty($errorActionDocument)) { + $this->message .= print_r($errorActionDocument,true); + } + } + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $this->message .= 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + } + } + + // Fonction permettant d'initialiser le job + public function initJob($paramJob) { + $this->paramJob = $paramJob; + $this->id = uniqid('', true); + $this->start = microtime(true); + + // Check if a job is already running + $sqlJobOpen = "SELECT * FROM Job WHERE job_status = 'Start' LIMIT 1"; + $stmt = $this->connection->prepare($sqlJobOpen); + $stmt->execute(); + $job = $stmt->fetch(); // 1 row + // Error if one job is still running + if (!empty($job)) { + $this->message .= $this->tools->getTranslation(array('messages', 'rule', 'another_task_running')).';'.$job['job_id']; + return false; + } + // Create Job + $insertJob = $this->insertJob(); + if ($insertJob) { + $this->createdJob = true; + return true; + } + else { + $this->message .= 'Failed to create the Job in the database'; + return false; + } + } + + // Permet de clôturer un job + public function closeJob() { + // Get job data + $this->logData = $this->getLogData(); + + // Update table job + return $this->updateJob(); + } + + + // Permet d'exécuter des jobs manuellement depuis Myddleware + public function actionMassTransfer($event,$param) { + if (in_array($event, array('rerun','cancel'))) { + // Pour ces 2 actions, l'event est le premier paramètre et ce sont les ids des cocuments qui sont envoyés dans le $param + $paramJob[] = $event; + $paramJob[] = implode(',',$param); + return $this->runBackgroundJob('massaction',$paramJob); + } + else { + return 'Action '.$event.' unknown. Failed to run this action. '; + } + } + + // Lancement d'un job manuellement en arrière plan + protected function runBackgroundJob($job,$param) { + try{ + // Création d'un fichier temporaire + $guid = uniqid(); + + // Formatage des paramètres + $params = implode(' ',$param); + + // récupération de l'exécutable PHP, par défaut c'est php + $php = $this->container->getParameter('php'); + if (empty($php['executable'])) { + $php['executable'] = 'php'; + } + + //Create your own folder in the cache directory + $fileTmp = $this->container->getParameter('kernel.cache_dir') . '/myddleware/job/'.$guid.'.txt'; + $fs = new Filesystem(); + try { + $fs->mkdir(dirname($fileTmp)); + } catch (IOException $e) { + throw new \Exception ("An error occured while creating your directory"); + } + exec($php['executable'].' '.__DIR__.'/../../../../app/console myddleware:'.$job.' '.$params.' --env=prod > '.$fileTmp.' &', $output); + $cpt = 0; + // Boucle tant que le fichier n'existe pas + while (!file_exists($fileTmp)) { + if($cpt >= 29) { + throw new \Exception ('Failed to run the job.'); + } + sleep(1); + $cpt++; + } + + // Boucle tant que l id du job n'est pas dans le fichier (écris en premier) + $file = fopen($fileTmp, 'r'); + $idJob = fread($file, 23); + fclose($file); + while (empty($idJob)) { + if($cpt >= 29) { + throw new \Exception ('No task id given.'); + } + sleep(1); + $file = fopen($fileTmp, 'r'); + $idJob = fread($file, 23); + fclose($file); + $cpt++; + } + // Renvoie du message en session + $session = new Session(); + $session->set( 'info', array(''.$this->container->get('translator')->trans('session.task.msglink').'. '.$this->container->get('translator')->trans('session.task.msginfo'))); + return $idJob; + } catch (\Exception $e) { + $session = new Session(); + $session->set( 'info', array($e->getMessage())); // Vous venez de lancer une nouvelle longue tâche. Elle est en cours de traitement. + return false; + } + } + + // Fonction permettant d'annuler massivement des documents + public function massAction($action,$idsDoc) { + if (empty($idsDoc)) { + $this->message .= 'No Ids in parameters of the job massAction.'; + return false; + } + + try { + // Formatage du tableau d'idate + $idsDocArray = explode(',',$idsDoc); + $queryIn = '('; + foreach ($idsDocArray as $idDoc) { + $queryIn .= "'".$idDoc."',"; + } + $queryIn = rtrim($queryIn,','); + $queryIn .= ')'; + + // Création de la requête + $sqlParams = " SELECT + Documents.id, + Documents.rule_id + FROM Documents + INNER JOIN Rule + ON Documents.rule_id = Rule.rule_id + WHERE + Documents.global_status IN ('Open','Error') + AND Documents.id IN $queryIn + ORDER BY Rule.rule_id"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $documents = $stmt->fetchAll(); + + if(!empty($documents)) { + include_once 'rule.php'; + $param['ruleId'] = ''; + foreach ($documents as $document) { + // Chargement d'une nouvelle règle que si nécessaire + if ($param['ruleId'] != $document['rule_id']) { + $param['ruleId'] = $document['rule_id']; + $param['jobId'] = $this->id; + $rule = new rule($this->logger, $this->container, $this->connection, $param); + } + $errorActionDocument = $rule->actionDocument($document['id'],$action); + if (!empty($errorActionDocument)) { + $this->message .= print_r($errorActionDocument,true); + } + } + } + else { + $this->message .= 'No Document Open or in Error in parameters of the job massAction.'; + return false; + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + public function getRules() { + try { + $sqlParams = " SELECT rule_name_slug + FROM RuleOrder + INNER JOIN Rule + ON Rule.rule_id = RuleOrder.rule_id + WHERE + Rule.rule_active = 1 + AND Rule.rule_deleted = 0 + ORDER BY RuleOrder.rod_order ASC"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $rules = $stmt->fetchAll(); + if(!empty($rules)) { + foreach ($rules as $rule) { + $ruleOrder[] = $rule['rule_name_slug']; + } + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + return false; + } + if (empty($ruleOrder)) { + return null; + } + return $ruleOrder; + } + + // Fonction permettant de définir un ordre dans le lancement des règles + public function orderRules() { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + // Récupération de toutes les règles avec leurs règles liées (si plusieurs elles sont toutes au même endroit) + // Si la règle n'a pas de relation on initialise l'ordre à 1 sinon on met 99 + $sql = "SELECT + Rule.rule_id, + GROUP_CONCAT(RuleRelationShips.rrs_field_id SEPARATOR ';') rrs_field_id, + IF(RuleRelationShips.rrs_field_id IS NULL, '1', '99') rule_order + FROM Rule + LEFT OUTER JOIN RuleRelationShips + ON Rule.rule_id = RuleRelationShips.rule_id + GROUP BY Rule.rule_id"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $rules = $stmt->fetchAll(); + if (!empty($rules)) { + // Création d'un tableau en clé valeur et sauvegarde d'un tableau de référence + $ruleKeyVakue = array(); + foreach ($rules as $rule) { + $ruleKeyVakue[$rule['rule_id']] = $rule['rule_order']; + $rulesRef[$rule['rule_id']] = $rule; + } + + // On calcule les priorité tant que l'on a encore des priorité 99 + // On fait une condition sur le $i pour éviter une boucle infinie + $i = 0; + while ($i < 20 && array_search('99', $ruleKeyVakue)!==false) { + $i++; + // Boucles sur les régles + foreach ($rules as $rule) { + $order = 0; + // Si on est une règle sans ordre + if($rule['rule_order'] == '99') { + // Récupération des règles liées et recherche dans le tableau keyValue + $rulesLink = explode(";", $rule['rrs_field_id']); + foreach ($rulesLink as $ruleLink) { + if( + !empty($ruleKeyVakue[$ruleLink]) + && $ruleKeyVakue[$ruleLink] > $order + ) { + $order = $ruleKeyVakue[$ruleLink]; + } + } + // Si toutes les règles trouvées ont une priorité autre que 99 alors on affecte à la règle la piorité +1 dans les tableaux de références + if ($order < 99) { + $ruleKeyVakue[$rule['rule_id']] = $order+1; + $rulesRef[$rule['rule_id']]['rule_order'] = $order+1; + } + } + } + $rules = $rulesRef; + } + + // On vide la table RuleOrder + $sql = "DELETE FROM RuleOrder"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + + //Mise à jour de la table + $insert = "INSERT INTO RuleOrder VALUES "; + foreach ($ruleKeyVakue as $key => $value) { + $insert .= "('$key','$value'),"; + } + // Suppression de la dernière virgule + $insert = rtrim($insert,','); + $stmt = $this->connection->prepare($insert); + $stmt->execute(); + } + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to update table RuleOrder : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error($this->message); + return false; + } + } + + public function generateTemplate($nomTemplate,$descriptionTemplate,$rulesId) { + include_once 'template.php'; + $templateString = ''; + if (!empty($rulesId)) { + $first = true; + $guidTemplate = uniqid(); + $template = new template($this->logger, $this->container, $this->connection); + foreach($rulesId as $ruleId) { + if ($first === true) { + $templateString .= $template->generateTemplateHeader($nomTemplate,$descriptionTemplate,$ruleId,$guidTemplate); + $first = false; + } + $generateTemplate = $template->generateTemplateRule($ruleId,$guidTemplate); + if (empty($generateTemplate['error'])) { + $templateString .= $generateTemplate['sql']; + } + else { + return array('done' => false, 'error' => $generateTemplate['error']); + } + } + // Ecriture du fichier + $file = __DIR__.'/../Templates/'.$nomTemplate.'.sql'; + $fp = fopen($file, 'wb'); + if ($fp === false) { + return array('done' => false, 'error' => 'Failed to open the file'); + } + $fw = fwrite($fp,utf8_encode($templateString)); + if ($fw === false) { + return array('done' => false, 'error' => 'Failed to write into the file'); + } + else { + return array('done' => true, 'error' => ''); + } + } + return $templateString; + } + + public function refreshTemplate() { + include_once 'template.php'; + $template = new template($this->logger, $this->container, $this->connection); + return $template->refreshTemplate(); + } + + // Permet d'indiquer que le job est lancé manuellement + protected function setManual() { + if ($this->env == 'background') { + $this->manual = 0; + } + else { + $this->manual = 1; + } + } + + // Permet d'indiquer que le job est lancé manuellement + public function setConfigValue($name,$value) { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit + // Récupération de la valeur de la config + $select = " SELECT * FROM Config WHERE conf_name = '$name'"; + $stmt = $this->connection->prepare($select); + $stmt->execute(); + $config = $stmt->fetch(); + try { + // S'il n'existe pas on fait un INSERT sinon un UPDATE + if (empty($config)) { + $sqlParams = "INSERT INTO Config (conf_name, conf_value) VALUES (:name, :value)"; + } + else { + $sqlParams = "UPDATE Config SET conf_value = :value WHERE conf_name = :name"; + } + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue("value", $value); + $stmt->bindValue("name", $name); + $stmt->execute(); + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error( 'Failed to update the config name '.$name.' whithe the value '.$value.' : '.$e->getMessage() ); + echo 'Failed to update the config name '.$name.' whithe the value '.$value.' : '.$e->getMessage() ; + return false; + } + return true; + } + + // Permet d'indiquer que le job est lancé manuellement + public function getConfigValue($name) { + // Récupération de la valeur de la config + $select = " SELECT * FROM Config WHERE conf_name = '$name'"; + $stmt = $this->connection->prepare($select); + $stmt->execute(); + $config = $stmt->fetch(); + return $config['conf_value']; + } + + + // Send notification to receive statistique about myddleware data transfer + public function sendNotification() { + try { + // Get the email address for notification + $contactMail = $this->container->getParameter('notification_emailaddress'); + if (empty($contactMail)) { + throw new \Exception ('No email address for notification. Please add the parameter notification_emailaddress in the file app/config/config_background.yml.'); + } + + // Write the introduction + $textMail = $this->tools->getTranslation(array('email_notification', 'hello')).chr(10).chr(10).$this->tools->getTranslation(array('email_notification', 'introduction')).chr(10); + + // Récupération du nombre de données transférées depuis la dernière notification. On en compte qu'une fois les erreurs + $sqlParams = " SELECT + count(distinct Log.doc_id) cpt, + Documents.global_status + FROM Job + INNER JOIN Log + ON Log.job_id = Job.job_id + INNER JOIN Rule + ON Log.rule_id = Rule.rule_id + INNER JOIN Documents + ON Documents.id = Log.doc_id + WHERE + Job.job_begin BETWEEN (SELECT MAX(job_begin) FROM Job WHERE job_param = 'notification' AND job_end >= job_begin) AND NOW() + AND ( + Documents.global_status != 'Error' + OR ( + Documents.global_status = 'Error' + AND Documents.date_modified BETWEEN (SELECT MAX(job_begin) FROM Job WHERE job_param = 'notification' AND job_end >= job_begin) AND NOW() + ) + ) + GROUP BY Documents.global_status"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $cptLogs = $stmt->fetchAll(); + $job_open = 0; + $job_close = 0; + $job_cancel = 0; + $job_error = 0; + if (!empty($cptLogs)) { + foreach ($cptLogs as $cptLog) { + switch ($cptLog['global_status']) { + case 'Open': + $job_open = $cptLog['cpt']; + break; + case 'Error': + $job_error = $cptLog['cpt']; + break; + case 'Close': + $job_close = $cptLog['cpt']; + break; + case 'Cancel': + $job_cancel = $cptLog['cpt']; + break; + } + } + } + $textMail .= $this->tools->getTranslation(array('email_notification', 'transfer_success')).' '.$job_close.chr(10); + $textMail .= $this->tools->getTranslation(array('email_notification', 'transfer_error')).' '.$job_error.chr(10); + $textMail .= $this->tools->getTranslation(array('email_notification', 'transfer_open')).' '.$job_open.chr(10); + + // Récupération des règles actives + $sqlParams = " SELECT * + FROM Rule + WHERE + Rule.rule_active = 1 + AND Rule.rule_deleted = 0 + "; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $activeRules = $stmt->fetchAll(); + if (!empty($activeRules)) { + $textMail .= chr(10).$this->tools->getTranslation(array('email_notification', 'active_rule')).chr(10); + foreach ($activeRules as $activeRule) { + $textMail .= " - ".$activeRule['rule_name']." v".$activeRule['rule_version'].chr(10); + } + } + else { + $textMail .= chr(10).$this->tools->getTranslation(array('email_notification', 'no_active_rule')).chr(10); + } + + + // Get errors since the last notification + if ($job_error > 0) { + $sqlParams = " SELECT + Log.log_created, + Log.log_msg, + Log.doc_id, + Rule.rule_name + FROM Job + INNER JOIN Log + ON Log.job_id = Job.job_id + INNER JOIN Rule + ON Log.rule_id = Rule.rule_id + INNER JOIN Documents + ON Documents.id = Log.doc_id + WHERE + Job.job_begin BETWEEN (SELECT MAX(job_begin) FROM Job WHERE job_param = 'notification' AND job_end >= job_begin) AND NOW() + AND Documents.date_modified BETWEEN (SELECT MAX(job_begin) FROM Job WHERE job_param = 'notification' AND job_end >= job_begin) AND NOW() + AND Documents.global_status = 'Error' + ORDER BY Log.log_created ASC + LIMIT 100 "; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $logs = $stmt->fetchAll(); + + if (count($logs) == 100) { + $textMail .= chr(10).chr(10).$this->tools->getTranslation(array('email_notification', '100_first_erros')).chr(10); + } + else { + $textMail .= chr(10).chr(10).$this->tools->getTranslation(array('email_notification', 'error_list')).chr(10); + } + foreach ($logs as $log) { + $textMail .= " - Règle $log[rule_name], id transfert $log[doc_id], le $log[log_created] : $log[log_msg]".chr(10); + } + } + + $textMail .= chr(10).$this->tools->getTranslation(array('email_notification', 'best_regards')).chr(10).$this->tools->getTranslation(array('email_notification', 'signature')); + $message = \Swift_Message::newInstance() + ->setSubject($this->tools->getTranslation(array('email_notification', 'subject'))) + ->setFrom('no-reply@myddleware.com') + ->setTo($contactMail) + ->setBody($textMail) + ; + $send = $this->container->get('mailer')->send($message); + if (!$send) { + $this->logger->error('Failed to send email : '.$textMail.' to '.$contactMail); + throw new \Exception ('Failed to send email : '.$textMail.' to '.$contactMail); + } + return true; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $this->message .= $e->getMessage(); + return false; + } + } + + // Permet de supprimer toutes les données des tabe source, target et history en fonction des paramètre de chaque règle + public function clearData() { + // Récupération de chaque règle et du paramètre de temps de suppression + $sqlParams = " SELECT + Rule.rule_id, + Rule.rule_name_slug, + Rule.rule_version, + RuleParams.rulep_value days + FROM Rule + INNER JOIN RuleParams + ON Rule.rule_id = RuleParams.rule_id + WHERE + RuleParams.rulep_name = 'delete'"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $rules = $stmt->fetchAll(); + if (!empty($rules)) { + // Boucle sur toutes les règles + foreach ($rules as $rule) { + $tableId = array(); + $tableId['z_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_source'] = 'id_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_source'; + $tableId['z_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_target'] = 'id_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_target'; + $tableId['z_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_history'] = 'id_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_history'; + + foreach ($tableId as $table => $id) { + $this->connection->beginTransaction(); + try { + $deleteSource = " + DELETE $table + FROM Documents + INNER JOIN $table + ON Documents.id = $table.$id + WHERE + Documents.rule_id = '$rule[rule_id]' + AND Documents.global_status IN ('Close','Cancel') + AND DATEDIFF(CURRENT_DATE( ),Documents.date_modified) >= $rule[days] + "; + $stmt = $this->connection->prepare($deleteSource); + $stmt->execute(); + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to clear data for the rule '.$rule['rule_id'].' : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error($this->message); + } + } + } + } + $this->clearJob(); + } + + // Permet de vider les log vides + protected function clearJob() { + $this->connection->beginTransaction(); + try { + // Suppression des jobs de transfert vide et des autres jobs qui datent de plus de nbDayClearJob jours + $deleteJob = " DELETE FROM Job + WHERE + job_status = 'End' + AND ( + ( + job_param NOT IN ('cleardata', 'backup', 'notification') + AND job_message IN ('', 'Another job is running. Failed to start job. ') + AND job_open = 0 + AND job_close = 0 + AND job_cancel = 0 + AND job_error = 0 + ) + OR ( + job_param IN ('cleardata', 'backup', 'notification') + AND job_message = '' + AND DATEDIFF(CURRENT_DATE( ),job_end) > '$this->nbDayClearJob' + ) + ) + "; + $stmt = $this->connection->prepare($deleteJob); + $stmt->execute(); + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->message .= 'Failed to clear data in table Job: '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error($this->message); + } + } + + // Récupération des données du job + protected function getLogData() { + try { + // Récupération du nombre de document envoyé et en erreur pour ce job + $this->logData['Close'] = 0; + $this->logData['Cancel'] = 0; + $this->logData['Open'] = 0; + $this->logData['Error'] = 0; + $this->logData['paramJob'] = $this->paramJob; + $sqlParams = " SELECT + count(distinct Documents.id) nb, + Documents.global_status + FROM Log + INNER JOIN Documents + ON Log.doc_id = Documents.id + WHERE + Log.job_id = :id + GROUP BY Documents.global_status"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue("id", $this->id); + $stmt->execute(); + $data = $stmt->fetchAll(); + if(!empty($data)) { + foreach ($data as $row) { + if($row['global_status'] == 'Close' ) { + $this->logData['Close'] = $row['nb']; + } + elseif($row['global_status'] == 'Error' ) { + $this->logData['Error'] = $row['nb']; + } + elseif($row['global_status'] == 'Cancel' ) { + $this->logData['Cancel'] = $row['nb']; + } + elseif($row['global_status'] == 'Open' ) { + $this->logData['Open'] = $row['nb']; + } + } + } + + // Récupération des solutions du job + $sqlParams = " SELECT + Connector_target.sol_id sol_id_target, + Connector_source.sol_id sol_id_source + FROM (SELECT DISTINCT rule_id FROM Log WHERE job_id = :id) rule_job + INNER JOIN Rule + ON rule_job.rule_id = Rule.rule_id + INNER JOIN Connector Connector_source + ON Connector_source.conn_id = Rule.conn_id_source + INNER JOIN Connector Connector_target + ON Connector_target.conn_id = Rule.conn_id_target"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue("id", $this->id); + $stmt->execute(); + $solutions = $stmt->fetchAll(); + $this->logData['solutions'] = ''; + if (!empty($solutions)) { + foreach ($solutions as $solution) { + $concatSolution[] = $solution['sol_id_target']; + $concatSolution[] = $solution['sol_id_source']; + } + $concatSolutions = array_unique($concatSolution); + // Mise au format pour la liste multi de Sugar + $concatSolutions = '^'.implode("^,^", $concatSolutions).'^'; + $this->logData['solutions'] = $concatSolutions; + } + + // Récupération de la durée du job + $time_end = microtime(true); + $this->logData['duration'] = round($time_end - $this->start,2); + + // récupération de l'id du job + $this->logData['myddlewareId'] = $this->id; + + // Indique si le job est lancé manuellement ou non + $this->logData['Manual'] = $this->manual; + + // Récupération des erreurs + $this->logData['jobError'] = $this->message; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $this->logData['jobError'] = 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + } + return $this->logData; + } + + // Mise à jour de la table Job + protected function updateJob() { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + $close = $this->logData['Close']; + $cancel = $this->logData['Cancel']; + $open = $this->logData['Open']; + $error = $this->logData['Error']; + $now = gmdate('Y-m-d H:i:s'); + $message = $this->message; + if (!empty($this->message)) { + $message = htmlspecialchars($this->message); + } + $query_header = "UPDATE Job + SET + job_end = :now, + job_status = 'End', + job_close = :close, + job_cancel = :cancel, + job_open = :open, + job_error = :error, + job_message = :message + WHERE job_id = :id"; + $stmt = $this->connection->prepare($query_header); + $stmt->bindValue("now", $now); + $stmt->bindValue("close", $close); + $stmt->bindValue("cancel", $cancel); + $stmt->bindValue("open", $open); + $stmt->bindValue("error", $error); + $stmt->bindValue("message", $message); + $stmt->bindValue("id", $this->id); + $stmt->execute(); + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error( 'Failed to update Job : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $this->message .= 'Failed to update Job : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + return false; + } + return true; + } + + protected function insertJob() { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + $now = gmdate('Y-m-d H:i:s'); + $query_header = "INSERT INTO Job (job_id, job_begin, job_status, job_param, job_manual) VALUES ('$this->id', '$now', 'Start', '$this->paramJob', '$this->manual')"; + $stmt = $this->connection->prepare($query_header); + $stmt->execute(); + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error( 'Failed to create Job : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $this->message .= 'Failed to create Job : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + return false; + } + return true; + } + +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/job.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class job extends jobcore { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/myddlewareFormulaFunctions.php b/src/Myddleware/RegleBundle/Classes/myddlewareFormulaFunctions.php new file mode 100644 index 000000000..36b8c9a35 --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/myddlewareFormulaFunctions.php @@ -0,0 +1,90 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +class myddlewareFormulaFunctionscore { + + private $names = array('changeTimeZone', 'changeFormatDate'); + + private $path = "Myddleware\RegleBundle\Classes\myddlewareFormulaFunctions::"; + + public function test() { + echo "test toto"; + } + + public function getNamesFunctions(){ + return $this->names; + } + + public function getPathFunctions() { + // Concaténation avant envoi du chemin avec le nom + $return = array(); + foreach ($this->names as $name) { + $return[] = $this->path.$name; + } + return $return; + } + + public static function changeTimeZone($dateToChange, $oldTimeZone, $newTimeZone) { + if (empty($dateToChange)) { + return null; + } + $date = date_create($dateToChange, timezone_open($oldTimeZone)); + date_timezone_set($date, timezone_open($newTimeZone)); + return date_format($date, "Y-m-d H:i:s"); + } + + public static function changeFormatDate($dateToChange, $oldFormat, $newFormat) { + if (empty($dateToChange)) { + return null; + } + $date = \DateTime::createFromFormat($oldFormat, $dateToChange); + return date_format($date, $newFormat); + } + + public static function changeValue($var, $arrayKeyToValue) { + if(in_array($var, array_keys($arrayKeyToValue))) { + $var = $arrayKeyToValue[$var]; + return $var; + } + return null; + } +} + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/myddlewareFormulaFunctions.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class myddlewareFormulaFunctions extends myddlewareFormulaFunctionscore { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/myddlewareFormulaV1.php b/src/Myddleware/RegleBundle/Classes/myddlewareFormulaV1.php new file mode 100644 index 000000000..940cb6722 --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/myddlewareFormulaV1.php @@ -0,0 +1,331 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +class myddlewareFormulaV1core { + + private $path = "Myddleware\RegleBundle\Classes\myddlewareFormulaFunctions::"; + + public $parse = array(); + public $functions; + + public function __construct() { + $this->parse['error'] = 0; // Erreur par défaut + $file = __DIR__.'/../Classes/myddlewareFormulaFunctions.php'; + if(file_exists($file)){ + require_once($file); + $this->functions = new myddlewareFormulaFunctions(); + } + } + + public function getNamespace() { + return __NAMESPACE__; + } + + public function init($formule) { + $this->parse['formule'] = $formule; + } + + // Retourne les tableaux en toute confiance + public function getSecure() { + return $this->secure; + } + + // Retourne tout le tableau parse + public function getParse() { + return $this->parse; + } + + // récupère tous les champs + private function getFields() { + preg_match_all("|{(.*)}|U",$this->parse['formule'],$fields, PREG_PATTERN_ORDER); + + if($fields[1]) { + foreach ($fields[1] as $f) { + $this->parse['field'][] = trim($f); + } + + if(isset($this->parse['field']) && count($this->parse['field']) > 0) { + $this->parse['field'] = array_unique($this->parse['field']); + $this->parse['error'] = $this->verifRegexSecure($this->parse['field'],$this->parse['error']); + } + } + } + + // récupère tous les champs textes + private function getText() { + preg_match_all('|"(.*)"|U',$this->parse['formule'],$txt, PREG_PATTERN_ORDER); + + if($txt[1]) { + + foreach ($txt[1] as $t) { + if($t == ' ') { + $this->parse['text'][] = $t; + } + else { + $string = trim($t); + $this->parse['text'][] = $string; + } + } + + if(isset($this->parse['text']) && count($this->parse['text']) > 0) { + $this->parse['text'] = array_unique($this->parse['text']); + } + } + } + + // récupère toutes les méthodes + private function getMethode() { + $stringFunc = $this->parse['formuleConvert']; + + // enlève les variables + if(isset($this->parse['field'])) { + foreach ($this->parse['field'] as $field ) { + $stringFunc = str_replace('{'.$field.'}', '', $stringFunc); + } + } + + // enlève les chaines + if(isset($this->parse['text'])) { + foreach ($this->parse['text'] as $txt ) { + $txt = str_replace(';', '', $txt); + $stringFunc = str_replace(';', '', $stringFunc); + $stringFunc = str_replace('"'.$txt.'"', '', $stringFunc); // 0 space + + $stringFunc = str_replace('" '.$txt.'"', '', $stringFunc); // left + $stringFunc = str_replace('"'.$txt.' "', '', $stringFunc); // right + $stringFunc = str_replace('" '.$txt.' "', '', $stringFunc); // two + } + } + + $stringFunc = str_replace(array('.','?',',','=','','+','-','"',';'),'',$stringFunc); + $stringFunc = str_replace('[]',',',$stringFunc); + $stringFunc = str_replace('()',' ',$stringFunc); + $stringFunc = str_replace('(','',$stringFunc); + $stringFunc = str_replace(')','',$stringFunc); + $r = explode(' ',$stringFunc); + $r = implode(',',$r); + $r = explode(',',$r); + + if($r) { + foreach ($r as $k) { + $ktrim = trim($k); + if($ktrim != '' && strlen($k) > 2) { + if (!preg_match("#[0-9]#", $ktrim)) { + $this->parse['function'][] = $ktrim; + } + } + } + + if(isset($this->parse['function']) && count($this->parse['function']) > 0) { + $this->parse['function'] = array_unique($this->parse['function']); + $this->parse['error'] = $this->verifAutorisationSecure($this->secureFunction(),$this->parse['function'],$this->parse['error']); + } + + } + } + + // Autorisation des méthodes + private function secureFunction() { + // Récupère le chemin des fonctions de myddlewareFormulaFunctions.php + $pathFunctions = $this->functions->getPathFunctions(); + + // array("pow","exp","abs","sin","cos","tan"); MATHS + $array = array('mb_strtolower','trim','mb_strtoupper','round','ceil','abs','mb_substr','str_replace','preg_replace', 'strip_tags', 'date', 'utf8_encode', 'utf8_decode','html_entity_decode','htmlentities','htmlspecialchars'); + $const = array('ENT_COMPAT','ENT_QUOTES','ENT_NOQUOTES','ENT_HTML401','ENT_XML1','ENT_XHTML','ENT_HTML5'); + + return array_merge($array, $const, $pathFunctions); + } + + private function replaceStringFunction($string) { + $string = str_replace('lower(', 'mb_strtolower(', $string); + $string = str_replace('upper(', 'mb_strtoupper(', $string); + $string = str_replace('substr(', 'mb_substr(', $string); + $string = str_replace('replace(', 'str_replace(', $string); + $string = str_replace('striptags(', 'strip_tags(', $string); + $string = str_replace('utf8encode(', 'utf8_encode(', $string); + $string = str_replace('utf8decode(', 'utf8_decode(', $string); + $string = str_replace('htmlEntityDecode(', 'html_entity_decode(', $string); + $string = str_replace('htmlentities (', 'htmlentities (', $string); + $string = str_replace('htmlspecialchars (', 'htmlspecialchars (', $string); + return $string; + } + + // Change les méthodes + private function remplaceFunction() { + preg_match_all('|"(.*)"|U',$this->parse['formule'],$txt, PREG_PATTERN_ORDER); + + $string = $this->parse['formule']; + + if(is_array($txt[1]) && count($txt[1]) > 0 ) { + + $txt[1] = array_unique($txt[1]); + + $new_text = array(); + $i = 0; + foreach ($txt[1] as $formule_text) { + $new_text['txt'.$i] = '"'.$formule_text.'"'; + $i++; + } + + $i=0; + + foreach ($new_text as $formule_text) { + $string = str_replace($formule_text, '@@@txt'.$i.'@@@', $string); + $i++; + } + + // REPLACE FUNCTION ---------------------------------------- + $string = $this->replaceStringFunction($string); + // REPLACE FUNCTION ---------------------------------------- + + $i = 0; + foreach ($new_text as $index => $name) { + $string = str_replace('@@@txt'.$i.'@@@', $name, $string); + $i++; + } + } + else { + // REPLACE FUNCTION ---------------------------------------- + $string = $this->replaceStringFunction($string); + // REPLACE FUNCTION ---------------------------------------- + } + + + // str_replace sur toutes les fonctions de myddlewareFormulaFunctions.php + $names = $this->functions->getNamesFunctions(); + if(count($names) > 0) { + foreach ($names as $name) { + $string = str_replace($name, $this->path.$name, $string); + } + } + + $this->parse['formuleConvert'] = $string; + } + + // Verification des tableaux pour détecter les erreurs + private function verifAutorisationSecure($tabSecure,$tabListe,$error) { + if(count($tabListe) > 0) { + foreach ($tabListe as $l) { + if(!in_array($l, $tabSecure)) { + $error++; + } + } + + return $error; + } + else { + return $error; + } + } + + // Détecte si une chaine possède des accents + private function accent($string) { + if(preg_match("#[áàâäãåçéèêëíìîïñóòôöõúùûüýÿ]#", mb_strtolower($string)) == 1) { + return true; + } + else { + return false; + } + } + + // Détecte si un tableau n'est pas conforme + private function verifRegexSecure($tabListe,$error) { + if(count($tabListe) > 0) { + foreach ($tabListe as $l) { + + if( preg_match('#[^[:alnum:]_]#u', $l) || $this->accent($l) ) { + $error++; + } + } + + return $error; + } + else { + return $error; + } + } + + // Transformation et securite de la formule + private function secureFormule() { + $string = str_replace('{', '$', $this->parse['formuleConvert']); + $tab = array('}'); + $string = str_replace($tab, '', $string); + + // méthodes + $string = str_replace('[', '(', $string); + $string = str_replace(']', ')', $string); + + // ----------- secure + $string = trim($string); + $string = strip_tags($string); + + // ----- remove control characters ----- + $string = str_replace("\r", '', $string); // --- replace with empty space + $string = str_replace("\n", '', $string); // --- replace with empty space + $string = str_replace("\t", '', $string); // --- replace with empty space + + // ----- remove multiple spaces ----- + $string = trim(preg_replace('/ {2,}/', ' ', $string)); + + $this->parse['formuleConvert'] = $string; + } + + // Execute la formule + public function execFormule() { + if($this->parse['error'] == 0) { + return $this->parse['formuleConvert']; + } + else { + return false; + } + } + + // Genère la nouvelle formule + public function generateFormule() { + $this->remplaceFunction(); // remplace les vrais fonctions + $this->getFields(); // contrôle sur les champs + $this->getText(); // contrôle sur les chaines + $this->getMethode(); // contrôle sur les méthodes + $this->secureFormule(); // niveau securité + } +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/myddlewareFormulaV1.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class myddlewareFormulaV1 extends myddlewareFormulaV1core { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/permission.php b/src/Myddleware/RegleBundle/Classes/permission.php new file mode 100644 index 000000000..eee5f8ed0 --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/permission.php @@ -0,0 +1,77 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +use Symfony\Bridge\Monolog\Logger; // Gestion des logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Accède aux services +use Doctrine\DBAL\Connection; // Connexion BDD + +class permissioncore { + + protected $connection; + protected $container; + protected $logger; + + + public function __construct(Logger $logger, Container $container, Connection $dbalConnection) { + $this->logger = $logger; + $this->container = $container; + $this->connection = $dbalConnection; + } + + // Check if the current user is admin + public function isAdmin($id) { + try { + $sql = "SELECT id + FROM users + WHERE roles LIKE '%ROLE_ADMIN%'"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $result = $stmt->fetch(); + if (!empty($result['id'])) { + return true; + } + return false; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } +} + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/permission.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class permission extends permissioncore { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/rule.php b/src/Myddleware/RegleBundle/Classes/rule.php new file mode 100644 index 000000000..a1d918e2f --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/rule.php @@ -0,0 +1,1295 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +use Symfony\Bridge\Monolog\Logger; // Logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Service access +use Doctrine\DBAL\Connection; // Connection database + +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Filesystem\Filesystem; + +use Myddleware\RegleBundle\Classes\tools as MyddlewareTools; // Tools + +class rulecore { + + protected $connection; + protected $container; + protected $logger; + protected $em; + protected $ruleId; + protected $rule; + protected $ruleFields; + protected $ruleParams; + protected $sourceFields; + protected $targetFields; + protected $fieldsType; + protected $ruleRelationships; + protected $ruleFilters; + protected $solutionSource; + protected $solutionTarget; + protected $jobId; + protected $manual; + protected $key; + protected $limit = 100; + protected $tools; + + public function __construct(Logger $logger, Container $container, Connection $dbalConnection, $param) { + $this->logger = $logger; + $this->container = $container; + $this->connection = $dbalConnection; + $this->em = $this->container->get('doctrine')->getEntityManager(); + + if (!empty($param['ruleId'])) { + $this->ruleId = $param['ruleId']; + $this->setRule($this->ruleId); + } + if (!empty($param['jobId'])) { + $this->jobId = $param['jobId']; + } + if (!empty($param['manual'])) { + $this->manual = $param['manual']; + } + if (!empty($param['limit'])) { + $this->limit = $param['limit']; + } + $this->setRuleParams(); + $this->setRuleRelationships(); + $this->setRuleFields(); + $this->tools = new MyddlewareTools($this->logger, $this->container, $this->connection); + } + + public function setRule($idRule) { + $this->ruleId = $idRule; + if (!empty($this->ruleId)) { + $rule = "SELECT *, (SELECT rulep_value FROM RuleParams WHERE rule_id = :ruleId and rulep_name= 'mode') rule_mode FROM Rule WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($rule); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $this->rule = $stmt->fetch(); + } + } + + // Generate a document for the current rule for a specific id in the source application. We don't use the reference for the function read. + // If parameter readSource is false, it means that the data source are already in the parameter param, so no need to read in the source application + public function generateDocument($idSource, $readSource = true, $param = '', $idFiledName = 'id') { + try { + if ($readSource) { + // Connection to source application + $connexionSolution = $this->connexionSolution('source'); + if ($connexionSolution === false) { + throw new \Exception ('Failed to connect to the source solution.'); + } + + // Read data in the source application + $read['module'] = $this->rule['rule_module_source']; + $read['fields'] = $this->sourceFields; + $read['ruleParams'] = $this->ruleParams; + $read['rule'] = $this->rule; + $read['query'] = array($idFiledName => $idSource); + $dataSource = $this->solutionSource->read_last($read); + if (!$dataSource['done']) { + throw new \Exception ('Failed to read record '.$idSource.' in the module '.$read['module'].' of the source solution. '.(!empty($dataSource['error']) ? $dataSource['error'] : '')); + } + } + else { + $dataSource['values'] = $param['values']; + } + + // Generate document + $doc['rule'] = $this->rule; + $doc['ruleFields'] = $this->ruleFields; + $doc['ruleRelationships'] = $this->ruleRelationships; + $doc['data'] = $dataSource['values']; + $doc['jobId'] = $this->jobId; + $document = new document($this->logger, $this->container, $this->connection, $doc); + $createDocument = $document->createDocument(); + if (!$createDocument) { + throw new \Exception ('Failed to create document : '.$document->getMessage()); + } + return $document; + } catch (\Exception $e) { + $error = 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + $this->logger->error($error); + $errorObj = new \stdClass(); + $errorObj->error = $error; + return $errorObj; + } + } + + // Connect to the source or target application + public function connexionSolution($type) { + + try { + if ($type == 'source') { + $connId = $this->rule['conn_id_source']; + } + elseif ($type == 'target') { + $connId = $this->rule['conn_id_target']; + } + else { + return false; + } + + // Get the name of the application + $sql = "SELECT sol_name + FROM Connector + JOIN Solution USING( sol_id ) + WHERE conn_id = :connId"; + $stmt = $this->connection->prepare($sql); + $stmt->bindValue(":connId", $connId); + $stmt->execute(); + $r = $stmt->fetch(); + + // Get params connection + $sql = "SELECT conp_id, conn_id, conp_name, conp_value + FROM ConnectorParams + WHERE conn_id = :connId"; + $stmt = $this->connection->prepare($sql); + $stmt->bindValue(":connId", $connId); + $stmt->execute(); + $tab_params = $stmt->fetchAll(); + + $params = array(); + if(!empty($tab_params)) { + foreach ($tab_params as $key => $value) { + $params[$value['conp_name']] = $value['conp_value']; + $params['ids'][$value['conp_name']] = array('conp_id' => $value['conp_id'],'conn_id' => $value['conn_id']); + } + } + + // Connect to the application + if ($type == 'source') { + $this->solutionSource = $this->container->get('myddleware_rule.'.$r['sol_name']); + $loginResult = $this->solutionSource->login($params); + $c = (($this->solutionSource->connexion_valide) ? true : false ); + } + else { + $this->solutionTarget = $this->container->get('myddleware_rule.'.$r['sol_name']); + $loginResult = $this->solutionTarget->login($params); + $c = (($this->solutionTarget->connexion_valide) ? true : false ); + } + if(!empty($loginResult['error'])) { + return $loginResult; + } + + return $c; + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + return false; + } + } + + // Logout to the application + protected function logoutSolution($type) { + try { + if ($type == 'source') { + $this->solutionSource = $this->container->get('myddleware_rule.'.$r['sol_name']); + return $this->solutionSource->logout($params); + } + else { + $this->solutionTarget = $this->container->get('myddleware_rule.'.$r['sol_name']); + return $this->solutionTarget->logout($params); + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + return false; + } + } + + // Permet de mettre toutes les données lues dans le système source dans le tableau $this->dataSource + // Cette fonction retourne le nombre d'enregistrements lus + public function createDocuments() { + $readSource = null; + // Si la lecture pour la règle n'est pas désactivée + // Et si la règle est active et pas supprimée ou bien le lancement est en manuel + if ( + empty($this->ruleParams['disableRead']) + && ( + ( + $this->rule['rule_deleted'] == 0 + && $this->rule['rule_active'] == 1 + ) + || ( + $this->manual == 1 + ) + ) + ) { + // lecture des données dans la source + $readSource = $this->readSource(); + if (empty($readSource['error'])) { + $readSource['error'] = ''; + } + // Si erreur + if (!isset($readSource['count'])) { + return $readSource; + } + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit + try { + if ($readSource['count'] > 0) { + include_once 'document.php'; + $param['rule'] = $this->rule; + $param['ruleFields'] = $this->ruleFields; + $param['ruleRelationships'] = $this->ruleRelationships; + $i = 0; + if($this->dataSource['values']) { + + // Boucle sur chaque document + foreach ($this->dataSource['values'] as $row) { + if ($i >= 1000){ + $this->connection->commit(); // -- COMMIT TRANSACTION + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION suspend auto-commit + $i = 0; + } + $i++; + $param['data'] = $row; + $param['jobId'] = $this->jobId; + $param['fieldsType'] = $this->fieldsType; + $document = new document($this->logger, $this->container, $this->connection, $param); + $createDocument = $document->createDocument(); + if (!$createDocument) { + $readSource['error'] .= $document->getMessage(); + } + } + } + // Mise à jour de la date de référence si des documents ont été créés + $this->updateReferenceDate(); + } + // Rollback if the job has been manually stopped + if ($this->getJobStatus() != 'Start') { + throw new \Exception('The task has been stopped manually during the document creation. No document generated. '); + } + $this->connection->commit(); // -- COMMIT TRANSACTION + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error( 'Failed to create documents : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + $readSource['error'] = 'Failed to create documents : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'; + } + } + // On affiche pas d'erreur si la lecture est désactivée + elseif (empty($this->ruleParams['disableRead'])) { + $readSource['error'] = 'The rule '.$this->rule['rule_name_slug'].' version '.$this->rule['rule_version'].($this->rule['rule_deleted'] == 1 ? ' is deleted.' : ' is disabled.'); + } + return $readSource; + } + + protected function getJobStatus() { + $sqlJobDetail = "SELECT * FROM Job WHERE job_id = :jobId"; + $stmt = $this->connection->prepare($sqlJobDetail); + $stmt->bindValue(":jobId", $this->jobId); + $stmt->execute(); + $job = $stmt->fetch(); // 1 row + if (!empty($job['job_status'])) { + return $job['job_status']; + } + return false; + } + + // Permet de mettre à jour la date de référence pour ne pas récupérer une nouvelle fois les données qui viennent d'être écrites dans la cible + protected function updateReferenceDate() { + $date_ref = $this->dataSource['date_ref']; + $sqlDateReference = "UPDATE RuleParams SET rulep_value = :date_ref WHERE rulep_name = 'datereference' AND rule_id = :ruleId"; + $stmt = $this->connection->prepare($sqlDateReference); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->bindValue(":date_ref", $date_ref); + $stmt->execute(); + } + + protected function readSource() { + + $read['module'] = $this->rule['rule_module_source']; + $read['rule'] = $this->rule; + $read['date_ref'] = $this->ruleParams['datereference']; + $read['ruleParams'] = $this->ruleParams; + $read['fields'] = $this->sourceFields; + $read['offset'] = 0; + $read['limit'] = $this->limit; + $read['jobId'] = $this->jobId; + $read['manual'] = $this->manual; + // Ajout des champs source des relations de la règle + if (!empty($this->ruleRelationships)) { + foreach ($this->ruleRelationships as $ruleRelationship) { + $read['fields'][] = $ruleRelationship['rrs_field_name_source']; + } + } + + // si champs vide + if(!empty($read['fields'])) { + $connect = $this->connexionSolution('source'); + if ($connect === true) { + $this->dataSource = $this->solutionSource->read($read); + // Si on a $this->limit résultats et que la date de référence n'a pas changée alors on récupère les enregistrements suivants + // Récupération de la date de modification du premier enregistrement + $value['date_modified'] = ''; + if (!empty($this->dataSource['values'])) { + $value = current($this->dataSource['values']); + } + if ( + !empty($this->dataSource['count']) + && $this->dataSource['count'] == $this->limit + && $this->dataSource['date_ref'] == $value['date_modified'] + ) { + $i = 0; + $dataSource = $this->dataSource; + // On boucle tant que l'on a pas de modification de date... Il faut prendre tous les enregistrement s'il y en a plus de $this->limit qui ont été créés à la même heure. + while ( + $dataSource['count'] == $this->limit + && $dataSource['date_ref'] == $value['date_modified'] + ) { + // Gestion de l'offset + $i++; + $read['offset'] = $i*$this->limit; + // On récupère les enregistrements suivants + $dataSource = $this->solutionSource->read($read); + if(empty($dataSource) || $dataSource['count'] == 0) break; + // Sauvegarde des élément dans le tableau final + $this->dataSource['values'] = array_merge($this->dataSource['values'],$dataSource['values']); + $this->dataSource['count'] += $dataSource['count']; + $this->dataSource['date_ref'] = $dataSource['date_ref']; + } + } + // Logout (source solution) + if (!empty($this->solutionSource)) { + $loginResult = $this->solutionSource->logout(); + if (!$loginResult) { + $this->dataSource['error'] .= 'Failed to logout from the source solution'; + } + } + return $this->dataSource; + } + elseif (!empty($connect['error'])){ + return $connect; + } + else { + return array('error' => 'Failed to connect to the source with rule : '.$this->ruleId.' .' ); + } + } + return array('error' => 'No field to read in source system. '); + } + + // Permet de filtrer les nouveau documents d'une règle + public function filterDocuments($documents = null) { + include_once 'document.php'; + $response = array(); + + // Sélection de tous les docuements de la règle au statut 'New' si aucun document n'est en paramètre + if (empty($documents)) { + $documents = $this->selectDocuments('New'); + } + + // Pour tous les docuements sélectionnés on vérifie les prédécesseurs + if(!empty($documents)) { + $this->setRuleFilters(); + foreach ($documents as $document) { + $param['id_doc_myddleware'] = $document['id']; + $param['jobId'] = $this->jobId; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $response[$document['id']] = $doc->filterDocument($this->ruleFilters); + } + } + return $response; + } + + // Permet de contrôler si un document de la même règle pour le même enregistrement n'est pas close + // Si un document n'est pas clos alors le statut du docuement est mis à "pending" + public function ckeckPredecessorDocuments($documents = null) { + include_once 'document.php'; + $response = array(); + + // Sélection de tous les docuements de la règle au statut 'Filter_OK' si aucun document n'est en paramètre + if (empty($documents)) { + $documents = $this->selectDocuments('Filter_OK'); + } + // Pour tous les docuements sélectionnés on vérifie les prédécesseurs + if(!empty($documents)) { + foreach ($documents as $document) { + $param['id_doc_myddleware'] = $document['id']; + $param['jobId'] = $this->jobId; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $response[$document['id']] = $doc->ckeckPredecessorDocument(); + } + } + return $response; + } + + // Permet de contrôler si un document de la même règle pour le même enregistrement n'est pas close + // Si un document n'est pas clos alors le statut du docuement est mis à "pending" + public function ckeckParentDocuments($documents = null) { + include_once 'document.php'; + // Permet de charger dans la classe toutes les relations de la règle + $response = array(); + + // Sélection de tous les docuements de la règle au statut 'New' si aucun document n'est en paramètre + if (empty($documents)) { + $documents = $this->selectDocuments('Predecessor_OK'); + } + if(!empty($documents)) { + // Pour tous les docuements sélectionnés on vérifie les parents + foreach ($documents as $document) { + $param['id_doc_myddleware'] = $document['id']; + $param['jobId'] = $this->jobId; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $response[$document['id']] = $doc->ckeckParentDocument($this->ruleRelationships); + } + } + return $response; + } + + // Permet de contrôler si un docuement de la même règle pour le même enregistrement n'est pas close + // Si un document n'est pas clos alors le statut du docuement est mis à "pending" + public function transformDocuments($documents = null){ + include_once 'document.php'; + // Permet de charger dans la classe toutes les relations de la règle + $response = array(); + + // Sélection de tous les docuements de la règle au statut 'New' si aucun document n'est en paramètre + if (empty($documents)) { + $documents = $this->selectDocuments('Relate_OK'); + } + if(!empty($documents)) { + // Transformation de tous les docuements sélectionnés + foreach ($documents as $document) { + $param['id_doc_myddleware'] = $document['id']; + $param['ruleFields'] = $this->ruleFields; + $param['ruleRelationships'] = $this->ruleRelationships; + $param['jobId'] = $this->jobId; + $param['key'] = $this->key; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $response[$document['id']] = $doc->transformDocument(); + } + } + return $response; + } + + + // Permet de récupérer les données de la cible avant modification des données + // 2 cas de figure : + // - Le document est un document de modification + // - Le document est un document de création mais la règle a un paramètre de vérification des données pour ne pas créer de doublon + public function getTargetDataDocuments($documents = null) { + include_once 'document.php'; + // Permet de charger dans la classe toutes les relations de la règle + $response = array(); + + // Sélection de tous les docuements de la règle au statut 'New' si aucun document n'est en paramètre + if (empty($documents)) { + $documents = $this->selectDocuments('Transformed'); + } + + if(!empty($documents)) { + // Connexion à la solution cible pour rechercher les données + $this->connexionSolution('target'); + + // Récupération de toutes les données dans la cible pour chaque document + foreach ($documents as $document) { + $param['id_doc_myddleware'] = $document['id']; + $param['solutionTarget'] = $this->solutionTarget; + $param['ruleFields'] = $this->ruleFields; + $param['jobId'] = $this->jobId; + $param['key'] = $this->key; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $response[$document['id']] = $doc->getTargetDataDocument(); + $response['doc_status'] = $doc->getStatus(); + } + } + return $response; + } + + public function sendDocuments() { + // Création des données dans la cible + $sendTarget = $this->sendTarget('C'); + // Modification des données dans la cible + $sendTarget = $this->sendTarget('U'); + // Logout target solution + if (!empty($this->solutionTarget)) { + $loginResult['error'] = $this->solutionTarget->logout(); + if (!$loginResult) { + $sendTarget .= 'Failed to logout from the target solution'; + } + } + return $sendTarget; + } + + public function actionDocument($id_document,$event) { + switch ($event) { + case 'rerun': + return $this->rerun($id_document); + break; + case 'cancel': + return $this->cancel($id_document); + break; + default: + return 'Action '.$event.' unknown. Failed to run this action. '; + } + } + + public function actionRule($event) { + switch ($event) { + case 'ALL': + return $this->runMyddlewareJob("ALL"); + break; + case 'ERROR': + return $this->runMyddlewareJob("ERROR"); + break; + case 'runMyddlewareJob': + return $this->runMyddlewareJob($this->rule['rule_name_slug']); + break; + default: + return 'Action '.$event.' unknown. Failed to run this action. '; + } + } + + // Permet de faire des contrôles dans Myddleware avant sauvegarde de la règle + // Si le retour est false, alors la sauvegarde n'est pas effectuée et un message d'erreur est indiqué à l'utilisateur + // data est de la forme : + // [ruleName] => nom + // [ruleVersion] => 001 + // [oldRule] => id de la règle précédente + // [connector] => Array ( [source] => 3 [cible] => 30 ) + // [content] => Array ( + // [fields] => Array ( [name] => Array ( [Date] => Array ( [champs] => Array ( [0] => date_entered [1] => date_modified ) [formule] => Array ( [0] => {date_entered}.{date_modified} ) ) [account_Filter] => Array ( [champs] => Array ( [0] => name ) ) ) ) + // [params] => Array ( [mode] => 0 ) ) + // [relationships] => Array ( [0] => Array ( [target] => compte_Reference [rule] => 54ea64f1601fc [source] => Myddleware_element_id ) ) + // [module] => Array ( [source] => Array ( [solution] => sugarcrm [name] => Accounts ) [target] => Array ( [solution] => bittle [name] => oppt_multi7 ) ) + // La valeur de retour est de a forme : array('done'=>false, 'message'=>'message erreur'); ou array('done'=>true, 'message'=>'') + public static function beforeSave($containeur,$data) { + // Contrôle sur la solution source + $solutionSource = $containeur->get('myddleware_rule.'.$data['module']['source']['solution']); + $check = $solutionSource->beforeRuleSave($data,'source'); + // Si OK contôle sur la solution cible + if ($check['done']) { + $solutionTarget = $containeur->get('myddleware_rule.'.$data['module']['target']['solution']); + $check = $solutionTarget->beforeRuleSave($data,'target'); + } + return $check; + } + + // Permet d'effectuer une action après la sauvegarde de la règle dans Myddleqare + // Mêmes paramètres en entrée que pour la fonction beforeSave sauf que l'on a ajouté les entrées ruleId et date de référence au tableau + public static function afterSave($containeur,$data) { + // Contrôle sur la solution source + $solutionSource = $containeur->get('myddleware_rule.'.$data['module']['source']['solution']); + $messagesSource = $solutionSource->afterRuleSave($data,'source'); + + $solutionTarget = $containeur->get('myddleware_rule.'.$data['module']['target']['solution']); + $messagesTarget = $solutionTarget->afterRuleSave($data,'target'); + + $messages = array_merge($messagesSource,$messagesTarget); + $data['testMessage'] = ''; + // Affichage des messages + if (!empty($messages)) { + $session = new Session(); + foreach ($messages as $message) { + if ($message['type'] == 'error') { + $errorMessages[] = $message['message']; + } + else { + $successMessages[] = $message['message']; + } + $data['testMessage'] .= $message['type'].' : '.$message['message'].chr(10); + } + if (!empty($errorMessages)) { + $session->set( 'error', $errorMessages); + } + if (!empty($successMessages)) { + $session->set( 'success', $successMessages); + } + } + } + + // Permet de récupérer les règles potentiellement biderectionnelle. + // Cette fonction renvoie les règles qui utilisent les même connecteurs et modules que la règle en cours mais en sens inverse (source et target inversées) + // On est sur une méthode statique c'est pour cela que l'on récupère la connexion e paramètre et non dans les attributs de la règle + public static function getBidirectionalRules($connection, $params) { + try { + // Récupération des règles opposées à la règle en cours de création + $queryBidirectionalRules = "SELECT + rule_id, + rule_name + FROM Rule + WHERE + conn_id_source = :conn_id_target + AND conn_id_target = :conn_id_source + AND rule_module_source = :rule_module_target + AND rule_module_target = :rule_module_source + AND rule_deleted = 0 + "; + $stmt = $connection->prepare($queryBidirectionalRules); + $stmt->bindValue(":conn_id_source", $params['connector']['source']); + $stmt->bindValue(":conn_id_target", $params['connector']['cible']); + $stmt->bindValue(":rule_module_source", $params['module']['source']); + $stmt->bindValue(":rule_module_target", $params['module']['cible']); + $stmt->execute(); + $bidirectionalRules = $stmt->fetchAll(); + + // Construction du tableau de sortie + if (!empty($bidirectionalRules)) { + $option[''] = ''; + foreach ($bidirectionalRules as $rule) { + $option[$rule['rule_id']] = $rule['rule_name']; + } + if (!empty($option)) { + return array( + array( + 'id' => 'bidirectional', + 'name' => 'bidirectional', + 'required' => false, + 'type' => 'option', + 'label' => 'create_rule.step3.params.sync', + 'option' => $option + ) + ); + } + } + } catch (\Exception $e) { + return null; + } + return null; + } + + // Permet d'annuler un docuement + protected function cancel($id_document) { + $param['id_doc_myddleware'] = $id_document; + $param['jobId'] = $this->jobId; + $param['key'] = $this->key; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $doc->updateStatus('Cancel'); + $session = new Session(); + $message = $doc->getMessage(); + + // Si on a pas de jobId cela signifie que l'opération n'est pas massive mais sur un seul document + // On affiche alors le message directement dans Myddleware + if (empty($this->jobId)) { + if (empty($message)) { + $session->set( 'success', array('Annulation du transfert effectuée avec succès.')); + } + else { + $session->set( 'error', array($doc->getMessage())); + } + } + } + + protected function runMyddlewareJob($ruleSlugName) { + try{ + $session = new Session(); + + // create temp file + $guid = uniqid(); + + // récupération de l'exécutable PHP, par défaut c'est php + $php = $this->container->getParameter('php'); + if (empty($php['executable'])) { + $php['executable'] = 'php'; + } + + $fileTmp = $this->container->getParameter('kernel.cache_dir') . '/myddleware/job/'.$guid.'.txt'; + $fs = new Filesystem(); + try { + $fs->mkdir(dirname($fileTmp)); + } catch (IOException $e) { + throw new \Exception ($this->tools->getTranslation(array('messages', 'rule', 'failed_create_directory'))); + } + + exec($php['executable'].' '.__DIR__.'/../../../../app/console myddleware:synchro '.$ruleSlugName.' --env=prod > '.$fileTmp.' &', $output); + $cpt = 0; + // Boucle tant que le fichier n'existe pas + while (!file_exists($fileTmp)) { + if($cpt >= 29) { + throw new \Exception ($this->tools->getTranslation(array('messages', 'rule', 'failed_running_job'))); + } + sleep(1); + $cpt++; + } + + // Boucle tant que l id du job n'est pas dans le fichier (écris en premier) + $file = fopen($fileTmp, 'r'); + $firstLine = fgets($file); + fclose($file); + while (empty($firstLine)) { + if($cpt >= 29) { + throw new \Exception ($this->tools->getTranslation(array('messages', 'rule', 'failed_get_task_id'))); + } + sleep(1); + $file = fopen($fileTmp, 'r'); + $firstLine = fgets($file); + fclose($file); + $cpt++; + } + + // transform all information of the first line in an arry + $result = explode(';',$firstLine); + // Renvoie du message en session + if ($result[0]) { + $session->set('info', array(''.$this->tools->getTranslation(array('messages', 'rule', 'open_running_task')).'.')); + } + else { + $session->set('error', array($result[1].(!empty($result[2]) ? ''.$this->tools->getTranslation(array('messages', 'rule', 'open_running_task')).'' : ''))); + } + return $result[0]; + } catch (\Exception $e) { + $session = new Session(); + $session->set( 'error', array($e->getMessage())); + return false; + } + } + + // Permet de relancer un document quelque soit son statut + protected function rerun($id_document) { + $session = new Session(); + $msg_error = array(); + $msg_success = array(); + $msg_info = array(); + // Récupération du statut du document + $param['id_doc_myddleware'] = $id_document; + $param['jobId'] = $this->jobId; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $status = $doc->getStatus(); + // Si la règle n'est pas chargée alors on l'initialise. + if (empty($this->ruleId)) { + $this->ruleId = $doc->getRuleId(); + $this->setRule($this->ruleId); + $this->setRuleRelationships(); + $this->setRuleParams(); + $this->setRuleFields(); + } + + // Si on a pas de job c'est que la relance est faite manuellement, il faut donc créer un job pour le flux relancé + $manual = false; + if (empty($this->jobId)) { + $manual = true; + include_once 'job.php'; + $job = new job($this->logger, $this->container, $this->connection); + if (!$job->initJob($this->rule['rule_name_slug'].' '.$id_document)) { + $session->set( 'error', array($job->message)); + return null; + } + else { + $this->jobId = $job->id; + } + } + + $response[$id_document] = false; + // On lance des méthodes différentes en fonction du statut en cours du document et en fonction de la réussite ou non de la fonction précédente + if (in_array($status,array('New','Filter_KO'))) { + $response = $this->filterDocuments(array(array('id' => $id_document))); + if ($response[$id_document] === true) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change => Filter_OK'; + } + elseif ($response[$id_document] == -1) { + $msg_info[] = 'Transfer id '.$id_document.' : Status change => Filter'; + } + else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Filter_KO'; + } + } + if ($response[$id_document] === true || in_array($status,array('Filter_OK','Predecessor_KO'))) { + $response = $this->ckeckPredecessorDocuments(array(array('id' => $id_document))); + if ($response[$id_document] === true) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change => Predecessor_OK'; + } + else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Predecessor_KO'; + } + } + if ($response[$id_document] === true || in_array($status,array('Predecessor_OK','Relate_KO'))) { + $response = $this->ckeckParentDocuments(array(array('id' => $id_document))); + if ($response[$id_document] === true) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change => Relate_OK'; + } + else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer => Relate_KO'; + } + } + if ($response[$id_document] === true || in_array($status,array('Relate_OK','Error_transformed'))) { + $response = $this->transformDocuments(array(array('id' => $id_document))); + if ($response[$id_document] === true) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : Transformed'; + } + else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_transformed'; + } + } + if ($response[$id_document] === true || in_array($status,array('Transformed','Error_history'))) { + $response = $this->getTargetDataDocuments(array(array('id' => $id_document))); + if ($response[$id_document] === true) { + if ($this->rule['rule_mode'] == 'S') { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : Send'; + } + else { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : '.$response['doc_status']; + } + } + else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_history'; + } + } + // Si la règle est en mode recherche alors on n'envoie pas de données + // Si on a un statut compatible ou si le doc vient de passer dans l'étape précédente et qu'il n'est pas no_send alors on envoie les données + if ( + $this->rule['rule_mode'] != 'S' + && ( + in_array($status,array('Ready_to_send','Error_sending')) + || ( + $response[$id_document] === true + && ( + empty($response['doc_status']) + || ( + !empty($response['doc_status']) + && $response['doc_status'] != 'No_send' + ) + ) + ) + ) + ){ + $response = $this->sendTarget('',$id_document); + if ( + !empty($response[$id_document]['id']) + && empty($response[$id_document]['error']) + ) { + $msg_success[] = 'Transfer id '.$id_document.' : Status change : Send'; + } + else { + $msg_error[] = 'Transfer id '.$id_document.' : Error, status transfer : Error_sending. '.$response[$id_document]['error']; + } + } + + // Si le job est manuel alors on clôture le job + if ($manual) { + if (!$job->closeJob()) { + $msg_error[] = 'Failed to update the job ('.$job->id.') : '.$job->message.''; + } + if (!empty($msg_error)) { + $session->set( 'error', $msg_error); + } + if (!empty($msg_success)) { + $session->set( 'success', $msg_success); + } + if (!empty($msg_info)) { + $session->set( 'info', $msg_info); + } + } + return $msg_error; + } + + protected function clearSendData($sendData) { + if (!empty($sendData)) { + foreach($sendData as $key => $value){ + unset($value['source_date_modified']); + unset($value['id_doc_myddleware']); + $sendData[$key] = $value; + } + return $sendData; + } + } + + protected function sendTarget($type, $documentId = null) { + // Permet de charger dans la classe toutes les relations de la règle + $response = array(); + $response['error'] = ''; + + // Le type peut-être vide das le cas d'un relancement de flux après une erreur + if (empty($type)) { + $documentData = $this->getDocumentData($documentId); + if (!empty($documentData['type'])) { + $type = $documentData['type']; + } + } + + // Récupération du contenu de la table target pour tous les documents à envoyer à la cible + $send['data'] = $this->getSendDocuments($type, $documentId); + $send['module'] = $this->rule['rule_module_target']; + $send['ruleId'] = $this->rule['rule_id']; + $send['rule'] = $this->rule; + $send['ruleFields'] = $this->ruleFields; + $send['ruleParams'] = $this->ruleParams; + $send['ruleRelationships'] = $this->ruleRelationships; + $send['fieldsType'] = $this->fieldsType; + $send['jobId'] = $this->jobId; + + // Si des données sont prêtes à être créées + if (!empty($send['data'])) { + // Connexion à la cible + $connect = $this->connexionSolution('target'); + if ($connect === true) { + // Création des données dans la cible + if ($type == 'C') { + // Permet de vérifier que l'on ne va pas créer un doublon dans la cible + $send['data'] = $this->checkDuplicate($send['data']); + $send['data'] = $this->clearSendData($send['data']); + $response = $this->solutionTarget->create($send); + } + // Modification des données dans la cible + elseif ($type == 'U') { + $send['data'] = $this->clearSendData($send['data']); + // permet de récupérer les champ d'historique, nécessaire pour l'update de SAP par exemple + $send['dataHistory'] = $this->getSendDocuments($type, $documentId, 'history'); + $send['dataHistory'] = $this->clearSendData($send['dataHistory']); + $response = $this->solutionTarget->update($send); + } + else { + $response[$documentId] = false; + $doc->setMessage('Type transfer '.$type.' unknown. '); + } + } + else { + $response[$documentId] = false; + $response['error'] = $connect['error']; + } + } + return $response; + } + + protected function checkDuplicate($transformedData) { + // Traitement si présence de champ duplicate + if (empty($this->ruleParams['duplicate_fields'])) { + return $transformedData; + } + + $duplicate_fields = explode(';',$this->ruleParams['duplicate_fields']); + $nameIdTarget = "id_".$this->rule['rule_name_slug']."_".$this->rule['rule_version']."_target"; + $searchDuplicate = array(); + // Boucle sur chaque donnée qui sera envoyée à la cible + foreach ($transformedData AS $rowTransformedData) { + // Stocke la valeur des champs duplicate concaténée + $concatduplicate = ''; + + // Récupération des valeurs de la source pour chaque champ de recherche + foreach($duplicate_fields as $duplicate_field) { + $concatduplicate .= $rowTransformedData[$duplicate_field]; + } + $searchDuplicate[$rowTransformedData[$nameIdTarget]] = array('concatKey' => $concatduplicate, 'source_date_modified' => $rowTransformedData['source_date_modified']); + } + + // Recherche de doublons dans le tableau searchDuplicate + // Obtient une liste de colonnes + foreach ($searchDuplicate as $key => $row) { + $concatKey[$key] = $row['concatKey']; + $source_date_modified[$key] = $row['source_date_modified']; + } + + // Trie les données par volume décroissant, edition croissant + // Ajoute $data en tant que dernier paramètre, pour trier par la clé commune + array_multisort($concatKey, SORT_ASC, $source_date_modified, SORT_ASC, $searchDuplicate); + + // Si doublon charge on charge les documents doublons, on récupère les plus récents et on les passe à transformed sans les envoyer à la cible. + // Le plus ancien est envoyé. + $previous = ''; + foreach ($searchDuplicate as $key => $value) { + if (empty($previous)) { + $previous = $value['concatKey']; + continue; + } + // Si doublon + if ($value['concatKey'] == $previous) { + $param['id_doc_myddleware'] = $key; + $param['jobId'] = $this->jobId; + $doc = new document($this->logger, $this->container, $this->connection, $param); + $doc->setMessage('Failed to send document because this record is already send in another document. To prevent create duplicate data in the target system, this document will be send in the next job.'); + $doc->setTypeError('W'); + $doc->updateStatus('Transformed'); + // Suppression du document dans l'envoi + unset($transformedData[$key]); + } + $previous = $value['concatKey']; + } + + if (!empty($transformedData)) { + return $transformedData; + } + return null; + } + + protected function selectDocuments($status, $type = '') { + try { + $query_documents = " SELECT * + FROM Documents + WHERE + rule_id = :ruleId + AND status = :status + ORDER BY Documents.source_date_modified ASC + LIMIT $this->limit + "; + $stmt = $this->connection->prepare($query_documents); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->bindValue(":status", $status); + $stmt->execute(); + return $stmt->fetchAll(); + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + // Permet de récupérer les données d'un document + protected function getDocumentData($documentId) { + try { + $query_document = "SELECT * FROM Documents WHERE id = :documentId"; + $stmt = $this->connection->prepare($query_document); + $stmt->bindValue(":documentId", $documentId); + $stmt->execute(); + $document = $stmt->fetch(); + if (!empty($document)) { + return $document; + } + else { + return false; + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + protected function getSendDocuments($type,$documentId,$table = 'target') { + $nameId = "id_".$this->rule['rule_name_slug']."_".$this->rule['rule_version']."_".$table; + $tableRule = "z_".$this->rule['rule_name_slug']."_".$this->rule['rule_version']."_".$table; + + // Si un document est en paramètre alors on filtre la requête sur le document + if (!empty($documentId)) { + $documentFilter = " Documents.id = '$documentId'"; + } + // Sinon on récupère tous les documents élligible pour l'envoi + else { + $documentFilter = " Documents.rule_id = '$this->ruleId' + AND Documents.status = 'Ready_to_send' + AND Documents.type = '$type' "; + } + // Sélection de tous les documents au statut transformed en attente de création pour la règle en cours + $sql = "SELECT $tableRule.* , Documents.id id_doc_myddleware, Documents.target_id, Documents.source_date_modified + FROM Documents + INNER JOIN $tableRule + ON Documents.id = $tableRule.$nameId + WHERE $documentFilter + ORDER BY Documents.source_date_modified ASC + LIMIT $this->limit"; + $stmt = $this->connection->prepare($sql); + $stmt->execute(); + $documents = $stmt->fetchAll(); + + foreach ($documents as $document) { + $return[$document['id_doc_myddleware']] = $document; + } + + if (!empty($return)) { + return $return; + } + return null; + } + + // Permet de charger tous les champs de la règle + protected function setRuleFields() { + + try { + // Lecture des champs de la règle + $sqlFields = "SELECT * + FROM RuleFields + WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($sqlFields); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $this->ruleFields = $stmt->fetchAll(); + + if($this->ruleFields) { + foreach ($this->ruleFields as $RuleField) { + // Plusieurs champs source peuvent être utilisé pour un seul champ cible + $fields = explode(";", $RuleField['rulef_source_field_name']); + foreach ($fields as $field) { + $this->sourceFields[] = ltrim($field); + } + $this->targetFields[] = ltrim($RuleField['rulef_target_field_name']); + } + } + + // Lecture des relations de la règle + if($this->ruleRelationships) { + foreach ($this->ruleRelationships as $ruleRelationship) { + $this->sourceFields[] = ltrim($ruleRelationship['rrs_field_name_source']); + $this->targetFields[] = ltrim($ruleRelationship['rrs_field_name_target']); + } + } + + // Dédoublonnage des tableaux + if (!empty($this->targetFields)) { + $this->targetFields = array_unique($this->targetFields); + } + if (!empty($this->sourceFields)) { + $this->sourceFields = array_unique($this->sourceFields); + } + + // Récupération des types de champs de la source + $sourceTable = "z_".$this->rule['rule_name_slug']."_".$this->rule['rule_version']."_source"; + $sqlParams = "SHOW COLUMNS FROM ".$sourceTable; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $sourceFields = $stmt->fetchAll(); + if (!empty($sourceFields)) { + foreach ($sourceFields as $sourceFiled) { + $this->fieldsType['source'][$sourceFiled['Field']] = $sourceFiled; + } + } + + // Récupération des types de champs de la target + $targetTable = "z_".$this->rule['rule_name_slug']."_".$this->rule['rule_version']."_target"; + $sqlParams = "SHOW COLUMNS FROM ".$targetTable; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $targetFileds = $stmt->fetchAll(); + if (!empty($targetFileds)) { + foreach ($targetFileds as $targetField) { + $this->fieldsType['target'][$targetField['Field']] = $targetField; + } + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + + } + + // Permet de charger tous les paramètres de la règle + protected function setRuleParams() { + + try { + $sqlParams = "SELECT * + FROM RuleParams + WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $ruleParams = $stmt->fetchAll(); + if($ruleParams) { + foreach ($ruleParams as $ruleParam) { + $this->ruleParams[$ruleParam['rulep_name']] = ltrim($ruleParam['rulep_value']); + } + } + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + + + // Permet de charger toutes les relations de la règle + protected function setRuleRelationships() { + try { + $sqlFields = "SELECT * + FROM RuleRelationShips + WHERE + rule_id = :ruleId + AND rule_id IS NOT NULL"; + $stmt = $this->connection->prepare($sqlFields); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $this->ruleRelationships = $stmt->fetchAll(); + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + // Permet de charger toutes les filtres de la règle + protected function setRuleFilters() { + try { + $sqlFields = "SELECT * + FROM RuleFilters + WHERE + rule_id = :ruleId"; + $stmt = $this->connection->prepare($sqlFields); + $stmt->bindValue(":ruleId", $this->ruleId); + $stmt->execute(); + $this->ruleFilters= $stmt->fetchAll(); + } catch (\Exception $e) { + $this->logger->error( 'Error : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + + // Parametre de la règle choix utilisateur + /* + array( + 'id' => 'datereference', + 'name' => 'datereference', + 'required' => true, + 'type' => 'text', + 'label' => 'solution.params.dateref', + 'readonly' => true + ), */ + public static function getFieldsParamUpd() { + return array(); + } + + // Parametre de la règle obligation du système par défaut + public static function getFieldsParamDefault($idSolutionSource = '',$idSolutionTarget = '') { + return array( + 'active' => false, + 'RuleParams' => array( + 'rate' => '5', + 'delete' => '60', + 'datereference' => date('Y-m-d').' 00:00:00' + ), + ); + } + + // Parametre de la règle en modification dans la fiche + public static function getFieldsParamView($idRule = '') { + return array( + array( + 'id' => 'datereference', + 'name' => 'datereference', + 'required' => true, + 'type' => 'text', + 'label' => 'solution.params.dateref' + ), + array( // clear data + 'id' => 'delete', + 'name' => 'delete', + 'required' => false, + 'type' => 'option', + 'label' => 'solution.params.delete', + 'option' => array ( + '0' => 'solution.params.0_day', + '1' => 'solution.params.1_day', + '7' => 'solution.params.7_day', + '14' => 'solution.params.14_day', + '30' => 'solution.params.30_day', + '60' => 'solution.params.60_day' + ), + ) + ); + } + +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/rule.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class rule extends rulecore { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/template.php b/src/Myddleware/RegleBundle/Classes/template.php new file mode 100644 index 000000000..9ec34b0b2 --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/template.php @@ -0,0 +1,480 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; + +use Symfony\Bridge\Monolog\Logger; // Gestion des logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Accède aux services +use Doctrine\DBAL\Connection; // Connexion BDD +use Symfony\Component\HttpFoundation\Session\Session; + + +class templatecore { + + protected $lang; + protected $idConnectorSource; + protected $idConnectorTarget; + protected $idUser; + protected $prefixRuleName; + + public function __construct(Logger $logger, Container $container, Connection $dbalConnection, $param = false) { + $this->logger = $logger; + $this->container = $container; + $this->connection = $dbalConnection; + if (!empty($param['lang'])) { + $this->lang = $param['lang']; + } + else { + $this->lang = 'EN'; + } + if (!empty($param['idConnectorSource'])) { + $this->idConnectorSource = $param['idConnectorSource']; + } + if (!empty($param['idConnectorTarget'])) { + $this->idConnectorTarget = $param['idConnectorTarget']; + } + if (!empty($param['prefixRuleName'])) { + $this->setPrefixRuleName($param['prefixRuleName']); + } + if (!empty($param['idUser'])) { + $this->idUser = $param['idUser']; + } + else{ + $this->idUser = 1; + } + } + + public function setIdConnectorSource($idConnectorSource) { + $this->idConnectorSource = $idConnectorSource; + } + + public function setIdConnectorTarget($idConnectorTarget) { + $this->idConnectorTarget = $idConnectorTarget; + } + + public function setIdUser($idUser) { + $this->idUser = $idUser; + } + + public function setLang($lang) { + $this->lang = $lang; + } + + // On enlève tous les caractères spéciaux du nom prefixRuleName + public function setPrefixRuleName($prefixRuleName) { + include_once 'tools.php'; + $this->prefixRuleName = tools::post_slug($prefixRuleName); + } + + // Permet de lister les templates pour les connecteurs selectionnés idConnectorSource et idConnectorTarget + public function getTemplates() { + return null; + } + + // Permet de convertir un template en règle lorsque l'utilisateur valide la sélection du template + public function convertTemplate($idTemplate) { + // Récupération des requêtes correspondant au template sélectionné + $queryTemplate = "SELECT tplq_query FROM TemplateQuery WHERE tplt_id = '$idTemplate'"; + $stmt = $this->connection->prepare($queryTemplate); + $stmt->execute(); + $queries = $stmt->fetchall(); + $nbRule = 0; + + // Lancement de toutes les requêtes du template + if(!empty($queries)) { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try{ + foreach($queries as $query) { + // Changement d'id rule à chaque nouvelle règle. La requête d'insertion de la règle est toujours la première + if (substr($query['tplq_query'],0,18) == 'INSERT INTO `Rule`') { + $idRule = uniqid(); + $nbRule++; + } + // Si la règle contient une relation, on récupère l'id de la règle créée pour mettre à jour la relation + if (substr($query['tplq_query'],0,31) == 'INSERT INTO `RuleRelationShips`') { + // Récupération du name_slug + $marqueurDebut = "#BEG#"; + $debutLien = strpos( $query['tplq_query'], $marqueurDebut ) + strlen( $marqueurDebut ); + $marqueurFin = "#END#"; + $finLien = strpos( $query['tplq_query'], $marqueurFin ); + $rule_name_slug = substr( $query['tplq_query'], $debutLien, $finLien - $debutLien ); + // Ajout du prefix de la règle + $rule_name_slug_prefix = $this->prefixRuleName.'_'.$rule_name_slug; + + // Récupération de l'id de la règle + $querySlug = " SELECT Rule.rule_id FROM Rule WHERE rule_name_slug = :rule_name_slug_prefix"; + $stmt = $this->connection->prepare($querySlug); + $stmt->bindValue("rule_name_slug_prefix", $rule_name_slug_prefix); + $stmt->execute(); + $rule = $stmt->fetch(); + + // remplacement de rule_name_slug par l'id de la règle dans le Myddleware en cours + $query['tplq_query'] = str_replace($marqueurDebut.$rule_name_slug.$marqueurFin, $rule['rule_id'],$query['tplq_query']); + } + // Remplacement des variables pour que les règles soient adaptées à la configuration de Myddleware du client + $query['tplq_query'] = str_replace('idConnectorSource', $this->idConnectorSource,$query['tplq_query']); + $query['tplq_query'] = str_replace('idConnectorTarget', $this->idConnectorTarget,$query['tplq_query']); + $query['tplq_query'] = str_replace('idUser', $this->idUser,$query['tplq_query']); + $query['tplq_query'] = str_replace('idRule', $idRule,$query['tplq_query']); + $query['tplq_query'] = str_replace('prefixRuleName', $this->prefixRuleName,$query['tplq_query']); + $stmt = $this->connection->prepare($query['tplq_query']); + $stmt->execute(); + } + + // On rafraichit la table order après la création des règles + include_once 'job.php'; + $job = new job($this->logger, $this->container, $this->connection); + $job->orderRules(); + + $this->connection->commit(); // -- COMMIT TRANSACTION + $session = new Session(); + $session->set( 'info', array("Nous vous recommandons de vous référer à l'article suivant afin de vous aider à activer ".($nbRule == 1 ? "la règle générée" : "les ".$nbRule." règles générées")." par le modèle prédéfini choisi.")); + } catch (\Exception $e) { + $session = new Session(); + $session->set( 'error', array("Erreur lors de le génération du template. Contactez le support support@crmconsult.fr",'Failed to generate template : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )')); + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + $this->logger->error( 'Failed to generate template : '.$e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )' ); + } + } + else { + $this->logger->error( 'Failed to create rule. There is no query for this template : '.$idTemplate.'.' ); + } + return true; + } + + public function generateTemplateHeader($nomTemplate,$descriptionTemplate,$ruleId,$guidTemplate) { + $sql = ''; + // Récupération des données de la règle + $query = " SELECT + Rule.*, + Connector_source.sol_id sol_source, + Connector_target.sol_id sol_target + FROM Rule + INNER JOIN Connector Connector_source + ON Rule.conn_id_source = Connector_source.conn_id + INNER JOIN Connector Connector_target + ON Rule.conn_id_target = Connector_target.conn_id + WHERE + rule_id = :ruleId"; + $stmt = $this->connection->prepare($query); + $stmt->bindValue("ruleId", $ruleId); + $stmt->execute(); + $rule = $stmt->fetch(); + if (empty($rule)) { + return array('sql' => '', 'error' => 'Failed to load the rule'); + } + + // Génération du dump de la table template + $sql .= "INSERT INTO Template VALUES ('$guidTemplate','$rule[sol_source]','$rule[sol_target]');".chr(10).chr(10); + $sql .= "INSERT INTO TemplateLang (`tpll_lang`, `tplt_id`, `tpll_name`, `tpll_description`) VALUES ('FR','$guidTemplate','$nomTemplate','$descriptionTemplate');".chr(10).chr(10); + return $sql; + } + + public function generateTemplateRule($ruleId,$guidTemplate) { + $sql = ''; + $query = " SELECT + Rule.*, + Connector_source.sol_id sol_source, + Connector_target.sol_id sol_target + FROM Rule + INNER JOIN Connector Connector_source + ON Rule.conn_id_source = Connector_source.conn_id + INNER JOIN Connector Connector_target + ON Rule.conn_id_target = Connector_target.conn_id + WHERE + rule_id = :ruleId"; + $stmt = $this->connection->prepare($query); + $stmt->bindValue("ruleId", $ruleId); + $stmt->execute(); + $rule = $stmt->fetch(); + + // Export table Rule + $sqlRule = $this->getSqlDump('Rule', $ruleId, $guidTemplate); + if (empty($sqlRule)) { + return array('sql' => '', 'error' => 'Failed to generate dump of table Rule'); + } + $sql .= $sqlRule; + + // Export table RuleParams + $sqlRuleParams = $this->getSqlDump('RuleParams', $ruleId, $guidTemplate); + if (empty($sqlRuleParams)) { + return array('sql' => '', 'error' => 'Failed to generate dump of table RuleParams'); + } + $sql .= $sqlRuleParams; + + // Export table RuleFilters + $sqlRuleFilters = $this->getSqlDump('RuleFilters', $ruleId, $guidTemplate); + if (!empty($sqlRuleFilters)) { + $sql .= $sqlRuleFilters; + } + + // Export table RuleRelationShips + $sqlRuleRelationShips = $this->getSqlDump('RuleRelationShips', $ruleId, $guidTemplate); + if (!empty($sqlRuleRelationShips)) { + $sql .= $sqlRuleRelationShips; + } + + // Export table RuleFields + $sqlRuleFields = $this->getSqlDump('RuleFields', $ruleId, $guidTemplate); + if (!empty($sqlRuleFields)) { + $sql .= $sqlRuleFields; + } + + $prefixTable = 'z_'.$rule['rule_name_slug'].'_'.$rule['rule_version'].'_'; + + // Récupération de la table source + $query = "SHOW CREATE TABLE ".$prefixTable.'source'; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + $tableSource = $stmt->fetch(); + if (empty($tableSource['Create Table'])) { + return array('sql' => '', 'error' => 'Failed to load the table source'); + } + // Remplacement du nom de la règle + $sqlSource = str_replace($rule['rule_name_slug'],'prefixRuleName_'.$rule['rule_name_slug'],$tableSource['Create Table']).';'; + $sql .= "INSERT INTO `TemplateQuery` (`tplt_id`, `tplq_query`) VALUES ('$guidTemplate', '$sqlSource');".chr(10).chr(10); + + // Récupération de la table target + $query = "SHOW CREATE TABLE ".$prefixTable.'target'; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + $tableTarget = $stmt->fetch(); + if (empty($tableTarget['Create Table'])) { + return array('sql' => '', 'error' => 'Failed to load the table target'); + } + // Remplacement du nom de la règle + $sqlTarget = str_replace($rule['rule_name_slug'],'prefixRuleName_'.$rule['rule_name_slug'],$tableTarget['Create Table']).';'; + $sql .= "INSERT INTO `TemplateQuery` (`tplt_id`, `tplq_query`) VALUES ('$guidTemplate', '$sqlTarget');".chr(10).chr(10); + + // Récupération de la table history + $query = "SHOW CREATE TABLE ".$prefixTable.'history'; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + $tableHistory = $stmt->fetch(); + if (empty($tableHistory['Create Table'])) { + return array('sql' => '', 'error' => 'Failed to load the table history'); + } + $sqlHistory = str_replace($rule['rule_name_slug'],'prefixRuleName_'.$rule['rule_name_slug'],$tableHistory['Create Table']).';'; + $sql .= "INSERT INTO `TemplateQuery` (`tplt_id`, `tplq_query`) VALUES ('$guidTemplate', '$sqlHistory');".chr(10).chr(10); + + return array('sql' => $sql, 'error' => ''); + } + + // Extraction des données d'un règle + protected function getSqlDump($table, $ruleId, $guidTemplate) { + $sql = ''; + $values = ''; + $break = false; + $nextDateReference = false; + $query = "SELECT * FROM $table WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($query); + $stmt->bindValue("ruleId", $ruleId); + $stmt->execute(); + $rows = $stmt->fetchall(); + if (!empty($rows)) { + $firstRow = true; + $fields = ''; + foreach ($rows as $row) { + // Prise en compte du cas où on n'a qu'une seule ligne dans le résultat de requête, on remet alors la ligne complète dans $row poru que la boucle suivant puisse écrire la requpete + if (is_string($row)) { + $row = $rows; + $break = true; + } + $firstfield = true; + $values .= '('; + //Ajout de tous les champs dans le dump + foreach ($row as $key => $value) { + // Si on est sur une table autre que Rule, on ne garde pas le premier paramètre qui est un id qui doit s'incrémenter + if ($firstfield && $table != 'Rule') { + $firstfield = false; + continue; + } + // Si on est sur la première ligne, on sauvegarde le header + if ($firstRow) { + $fields .= "`$key`,"; + } + // Pour certaine données on insère la clé et non la valeur car ces données seront des paramètres + if ($key == 'rule_id') { + $values .= "'idRule',"; + } + elseif ($key == 'conn_id_source') { + $values .= "'idConnectorSource',"; + } + elseif ($key == 'conn_id_target') { + $values .= "'idConnectorTarget',"; + } + // Par défaut une règle est inactive + elseif ($key == 'rule_active') { + $values .= "'0',"; + } + elseif ($key == 'rule_name') { + $values .= "'prefixRuleName_".$row['rule_name_slug']."',"; + } + elseif ($key == 'rule_name_slug') { + $values .= "'prefixRuleName_".$row['rule_name_slug']."',"; + } + elseif (in_array($key, array('rule_created_by','rule_modified_by'))) { + $values .= "'idUser',"; + } + // Les dates doivent être dynamiques + elseif(in_array($key, array('rule_date_created','rule_date_modified'))) { + $values .= "NOW(),"; + } + // La date de référence est égale à aujourd'hui à minuit + elseif ($nextDateReference) { + $values .= "CONCAT( CURDATE( ),' 00:00:00' ),"; + $nextDateReference = false; + } + // Il faut gérer les " dans les formule + elseif($key == 'rulef_formula') { + $values .= "'".addslashes($value)."',"; + } + // S'il s'agit d'une relation il faut mettre un code pour pouvoir mettre l'id de la règle lié + elseif($key == 'rrs_field_id') { + // Récupération du name_slug de la règle liée + $query = " SELECT Rule.rule_name_slug FROM Rule WHERE rule_id = :ruleId"; + $stmt = $this->connection->prepare($query); + $stmt->bindValue("ruleId", $value); + $stmt->execute(); + $rule = $stmt->fetch(); + $values .= "'#BEG#$rule[rule_name_slug]#END#',"; + } + else { + $values .= "'".$value."',"; + } + // Gestion de la date de référence + if ($value == 'datereference' && $table == 'RuleParams') { + $nextDateReference = true; + } + } + $firstRow = false; + // Suppression de la dernière virgule er gestion de la fin de ligne + $values = rtrim($values,','); + $values .= '),'.chr(10); + if ($break) { + break; + } + } + // Suppression de la dernière virgule er gestion de la fin de requête + $values = rtrim($values,chr(10)); + $values = rtrim($values,','); + $fields = rtrim($fields,','); + $sql = "INSERT INTO `$table` ($fields) VALUES $values "; + + // Création de la requête finale + $sql = "INSERT INTO `TemplateQuery` (`tplt_id`, `tplq_query`) VALUES ('$guidTemplate', \"$sql\");".chr(10).chr(10); + } + return $sql; + } + + public function refreshTemplate() { + $this->connection->beginTransaction(); // -- BEGIN TRANSACTION + try { + // Suppression des données dans les tables template + $clearTable = $this->clearTable(); + if ($clearTable['done'] === false) { + throw new \Exception( $clearTable['error'] ); + } + + // Permet de charger tous les templates + $loadTemplates = $this->loadTemplates(); + if ($loadTemplates['done'] === false) { + throw new \Exception( $loadTemplates['error'] ); + } + + $this->connection->commit(); // -- COMMIT TRANSACTION + return array('done' => true, 'error' => ''); + } catch (\Exception $e) { + $this->connection->rollBack(); // -- ROLLBACK TRANSACTION + return array('done' => false, 'error' => $e->getMessage()); + } + } + + // Suppression des données dans les tables template + protected function clearTable() { + try { + $query = "DELETE FROM Template"; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + + $query = "DELETE FROM TemplateLang"; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + + $query = "DELETE FROM TemplateQuery"; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + return array('done' => true, 'error' => ''); + } catch (\Exception $e) { + return array('done' => false, 'error' => $e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'); + } + } + + // Permet de charger tous les templates + protected function loadTemplates() { + try { + $dir = __DIR__.'/../Templates/'; + $d = dir($file = $dir); + while ($entry = $d->read()) { + if(!empty($entry)){ + $queryFile = file_get_contents($dir.$entry); + $queries = explode(";\n", $queryFile); + if (!empty($queries)) { + foreach ($queries as $query) { + if (!empty($query) && $query != chr(10)) { + $stmt = $this->connection->prepare($query); + $stmt->execute(); + } + } + } + } + } + // Permet de rattraper une erreur généré par 2 guillements dans les create table + $query = "UPDATE TemplateQuery SET tplq_query = REPLACE(tplq_query, 'NOT NULL DEFAULT \'', 'NOT NULL DEFAULT \'\'')"; + $stmt = $this->connection->prepare($query); + $stmt->execute(); + $d->close(); + return array('done' => true, 'error' => ''); + } catch (\Exception $e) { + return array('done' => false, 'error' => $e->getMessage().' '.__CLASS__.' Line : ( '.$e->getLine().' )'); + } + } +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/template.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class template extends templatecore { + + } +} +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Classes/tools.php b/src/Myddleware/RegleBundle/Classes/tools.php new file mode 100644 index 000000000..579abac57 --- /dev/null +++ b/src/Myddleware/RegleBundle/Classes/tools.php @@ -0,0 +1,172 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Classes; +use Symfony\Bridge\Monolog\Logger; // Gestion des logs +use Symfony\Component\DependencyInjection\ContainerInterface as Container; // Accède aux services +use Doctrine\DBAL\Connection; // Connexion BDD +use Symfony\Component\Yaml\Yaml; // Read yml file + +class toolscore { + + protected $connection; + protected $container; + protected $logger; + + protected $language; + protected $tranlations; + + public function __construct(Logger $logger, Container $container, Connection $dbalConnection) { + $this->logger = $logger; + $this->container = $container; + $this->connection = $dbalConnection; + } + + // Compose une liste html avec les options + public static function composeListHtml($array,$phrase = false) { + $r=""; + if($array) { + asort( $array ); + if($phrase) { + $r.=''; + $r.=''; + } + + foreach ($array as $k => $v) { + if($v != '') { + $r.=''; + } + } + } + else { + $r.=''; + } + + return $r; + } + + + public static function post_slug($str) { + $str = utf8_decode($str); + $str = strtr($str, utf8_decode('ÁÀÂÄÃÅÇÉÈÊËÍÏÎÌÑÓÒÔÖÕÚÙÛÜÝ'), utf8_decode('AAAAAACEEEEEIIIINOOOOOUUUUY')); + $str = strtr($str, utf8_decode('áàâäãåçéèêëíìîïñóòôöõúùûüýÿ'), utf8_decode('aaaaaaceeeeiiiinooooouuuuyy')); + return strtolower(preg_replace(array('/[^a-zA-Z0-9 -]/', '/[ -]+/', '/^-|-$/'), array('', '_', ''), $str)); + } + + + // Permet de renvoyer les champs d'un table avec leur type + public function describeTable($table) { + // Création de la requête + $sqlParams = "DESCRIBE $table"; + $stmt = $this->connection->prepare($sqlParams); + $stmt->execute(); + $fields = $stmt->fetchAll(); + if (!empty($fields)) { + foreach ($fields as $field) { + $result[$field['Field']] = $field; + } + return $result; + } + return null; + } + + // Allow translation from php classes + public function getTranslation($textArray) { + try { + $result = ''; + // Get the current language + $this->language = $this->container->getParameter('locale'); + + // Get the translation for the current language + if (empty($this->tranlations)) { + $this->tranlations = Yaml::parse(file_get_contents(__DIR__.'/../Resources/translations/messages.'.$this->language.'.yml')); + } + // Search the translation + if (!empty($this->tranlations)) { + // Get the first level + if (!empty($this->tranlations[$textArray[0]])) { + $result = $this->tranlations[$textArray[0]]; + } + // Get the next levels + $nbLevel = sizeof($textArray); + for($i = 1; $i < $nbLevel;$i++) { + if (!empty($result[$textArray[$i]])) { + $result = $result[$textArray[$i]]; + } + else { + $result = ''; + break; + } + } + } + // Return the input text if the translation hasn't been found + if (empty($result)) { + $result = implode(' - ',$textArray); + } + } catch (\Exception $e) { + $result = implode(' - ',$textArray); + } + return $result; + } + + // Change Myddleware parameters + public function changeMyddlewareParameter($nameArray, $value) { + $myddlewareParameters = Yaml::parse(file_get_contents($this->container->getParameter('kernel.root_dir').'/config/parameters.yml')); + // Search the translation + if (!empty($myddlewareParameters)) { + $nbLevel = sizeof($nameArray); + switch ($nbLevel) { + case 1: + $myddlewareParameters['parameters'][$nameArray[0]] = $value; + break; + case 2: + $myddlewareParameters['parameters'][$nameArray[0]][$nameArray[1]] = $value; + break; + case 3: + $myddlewareParameters['parameters'][$nameArray[0]][$nameArray[1]][$nameArray[2]] = $value; + break; + } + } + $new_yaml = \Symfony\Component\Yaml\Yaml::dump($myddlewareParameters, 4); + file_put_contents($this->container->getParameter('kernel.root_dir').'/config/parameters.yml', $new_yaml); + } +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Classes/tools.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class tools extends toolscore { + + } +} + +?> \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/RerunErrorCommand.php b/src/Myddleware/RegleBundle/Command/RerunErrorCommand.php new file mode 100644 index 000000000..eee0862c6 --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/RerunErrorCommand.php @@ -0,0 +1,75 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class RerunErrorCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('myddleware:rerunerror') + ->setDescription('Synchronisation des données') + ->addArgument('limit', InputArgument::REQUIRED, "Nombre maximum de flux en erreur traité") + ->addArgument('attempt', InputArgument::REQUIRED, "Nombre maximum de tentative") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + try { + $logger = $this->getContainer()->get('logger'); + $limit = $input->getArgument('limit'); + $attempt = $input->getArgument('attempt'); + // Récupération du Job + $job = $this->getContainer()->get('myddleware_job.job'); + + if ($job->initJob('ERROR')) { + // Premier paramètre : limite d'enregistrement traités + // Deuxième paramètre, limite d'erreur : si un flux a plus de tentative que le paramètre il n'est pas relancé + $job->runError( $limit , $attempt); + } + } + catch(\Exception $e) { + $job->message .= $e->getMessage(); + } + + // Close job if it has been created + if($job->createdJob === true) { + $job->closeJob(); + } + + // Display message on the console + if (!empty($job->message)) { + $output->writeln(''.$job->message.''); + $logger->error($job->message); + } + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/TaskCommand.php b/src/Myddleware/RegleBundle/Command/TaskCommand.php new file mode 100644 index 000000000..a264e6737 --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/TaskCommand.php @@ -0,0 +1,123 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class TaskCommand extends ContainerAwareCommand { + + protected function configure() + { + $this + ->setName('myddleware:synchro') + ->setDescription('Synchronisation des données') + ->addArgument('rule', InputArgument::REQUIRED, "Alias de la règle") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $step = 1; + try { + $logger = $this->getContainer()->get('logger'); + + // Source ------------------------------------------------- + // alias de la règle en params + $rule = $input->getArgument('rule'); + // Récupération du Job + $job = $this->getContainer()->get('myddleware_job.job'); + + if ($job->initJob($rule)) { + $output->writeln( '1;'.$job->id ); // Ne pas supprimer car nécessaire pour afficher les log d'un job manuel + + if (!empty($rule)) { + if ($rule == 'ERROR') { + // Premier paramètre : limite d'enregistrement traités + // Deuxième paramètre, limite d'erreur : si un flux a plus de tentative que le paramètre il n'est pas relancé + $job->runError( 50 , 100); + } + else { + // Envoi du job sur toutes les règles demandées. Si ALL est sélectionné alors on récupère toutes les règle dans leur ordre de lancement sinon on lance seulement la règle demandée. + if ($rule == 'ALL') { + $rules = $job->getRules(); + } + else { + $rules[] = $rule; + } + if (!empty($rules)) { + foreach ($rules as $key => $value) { + echo $value.chr(10); + $output->writeln($step.'-'.$value.' : Synchronisation de la règle : '.$value.''); $step++; + // Chargement des données de la règle + if ($job->setRule($value)) { + // Sauvegarde des données sources dans les tables de myddleware + $output->writeln($step.'-'.$value.' : Create documents.'); + $nb = $job->createDocuments(); + $output->writeln($step.'-'.$value.' : Number of documents created : '.$nb); + + // Permet de filtrer les documents + $job->filterDocuments(); + + // Permet de valider qu'aucun document précédent pour la même règle et le même id n'est pas bloqué + $job->ckeckPredecessorDocuments(); + + // Permet de valider qu'au moins un document parent(relation père) est existant + $job->ckeckParentDocuments(); + + // Permet de transformer les docuement avant d'être envoyés à la cible + $job->transformDocuments(); + + // Historisation des données avant modification dans la cible + $job->getTargetDataDocuments(); + + // Envoi des documents à la cible + $job->sendDocuments(); + } + } + } + } + } + } + } + catch(\Exception $e) { + $job->message .= $e->getMessage(); + } + + // Close job if it has been created + if($job->createdJob === true) { + $job->closeJob(); + } + + // Retour en console -------------------------------------- + if (!empty($job->message)) { + $output->writeln( '0;'.$job->message.''); + $logger->error( $job->message ); + } + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/clearDataCommand.php b/src/Myddleware/RegleBundle/Command/clearDataCommand.php new file mode 100644 index 000000000..ed0443f50 --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/clearDataCommand.php @@ -0,0 +1,65 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class clearDataCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('myddleware:cleardata') + ->setDescription('SUPPRESSION DES DONNEES DU CLIENT') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $logger = $this->getContainer()->get('logger'); + + $job = $this->getContainer()->get('myddleware_job.job'); + + if ($job->initJob('cleardata')) { + $job->clearData(); + } + + // Close job if it has been created + if($job->createdJob === true) { + $job->closeJob(); + } + + // Display message on the console + if (!empty($job->message)) { + $output->writeln(''.$job->message.''); + $logger->error($job->message); + } + } + + +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/generateTemplateCommand.php b/src/Myddleware/RegleBundle/Command/generateTemplateCommand.php new file mode 100644 index 000000000..044a68f4e --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/generateTemplateCommand.php @@ -0,0 +1,67 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Psr\Log\LoggerInterface; + +class generateTemplateCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('myddleware:generateTemplate') + ->setDescription('Génération de template') + ->addArgument('nomTemplate', InputArgument::REQUIRED, "nom") + ->addArgument('descriptionTemplate', InputArgument::REQUIRED, "nom") + ->addArgument('rulesId', InputArgument::REQUIRED, "nom") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + + $nomTemplate = $input->getArgument('nomTemplate'); + $rulesId = $input->getArgument('rulesId'); + $descriptionTemplate = $input->getArgument('descriptionTemplate'); + + $rulesId = explode("/", $rulesId); + + // Récupération du Job + $job = $this->getContainer()->get('myddleware_job.job'); + $result = $job->generateTemplate($nomTemplate,$descriptionTemplate,$rulesId); + if ($result['done']) { + $output->writeln( 'Template '.$nomTemplate.' generated.' ); + } + else { + $output->writeln( 'Failed to generate template '.$nomTemplate.' : '.$result['error'].'' ); + } + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/massActionCommand.php b/src/Myddleware/RegleBundle/Command/massActionCommand.php new file mode 100644 index 000000000..2479e02d3 --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/massActionCommand.php @@ -0,0 +1,84 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Psr\Log\LoggerInterface; + +class massActionCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('myddleware:massaction') + ->setDescription('Action massive sur les flux') + ->addArgument('action', InputArgument::REQUIRED, "Action") // id séparés par des ";" + ->addArgument('idsDoc', InputArgument::REQUIRED, "Ids de document") // id séparés par des ";" + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $step = 1; + try { + $logger = $this->getContainer()->get('logger'); + + // Récupération des paramètres + $action = $input->getArgument('action'); + $idsDoc = $input->getArgument('idsDoc'); + // Récupération du Job + $job = $this->getContainer()->get('myddleware_job.job'); + + if ($job->initJob('Mass '.$action)) { + $output->writeln( $job->id ); // Ne pas supprimer car nécessaire pour afficher les log d'un job manuel + + // Annulation en masse + $job->massAction($action,$idsDoc); + } + else { + $output->writeln( $job->id ); // Ne pas supprimer car nécessaire pour afficher les log d'un job manuel + } + } + catch(\Exception $e) { + $job->message .= $e->getMessage(); + } + + // Close job if it has been created + if($job->createdJob == true) { + $job->closeJob(); + } + + // Display message on the console + if (!empty($job->message)) { + $output->writeln(''.$job->message.''); + $logger->error($job->message); + } + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/notificationCommand.php b/src/Myddleware/RegleBundle/Command/notificationCommand.php new file mode 100644 index 000000000..a596f056e --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/notificationCommand.php @@ -0,0 +1,67 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Psr\Log\LoggerInterface; + +class notificationCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('myddleware:notification') + ->setDescription('DESCRIPTION') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $logger = $this->getContainer()->get('logger'); + $job = $this->getContainer()->get('myddleware_job.job'); + + if ($job->initJob('notification')) { + $job->sendNotification(); + } + + // Close job if it has been created + if($job->createdJob == true) { + $job->closeJob(); + } + + // Display message on the console + if (!empty($job->message)) { + $output->writeln(''.$job->message.''); + $logger->error($job->message); + } + } + + +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Command/refreshTemplateCommand.php b/src/Myddleware/RegleBundle/Command/refreshTemplateCommand.php new file mode 100644 index 000000000..f9c9980d9 --- /dev/null +++ b/src/Myddleware/RegleBundle/Command/refreshTemplateCommand.php @@ -0,0 +1,61 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +//use Symfony\Bridge\Monolog\Logger; +use Psr\Log\LoggerInterface; + +class refreshTemplateCommand extends ContainerAwareCommand +{ + protected function configure() + { + $this + ->setName('myddleware:refreshTemplate') + ->setDescription('Refresh de template') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + // Récupération du Job + $job = $this->getContainer()->get('myddleware_job.job'); + $result = $job->refreshTemplate(); + + if ($result['done']) { + $output->writeln( 'Refresh finished' ); + } + else { + $output->writeln( 'Failed to refresh template : '.$result['error'].'' ); + } + + } + +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Controller/AccountController.php b/src/Myddleware/RegleBundle/Controller/AccountController.php new file mode 100644 index 000000000..653987fa7 --- /dev/null +++ b/src/Myddleware/RegleBundle/Controller/AccountController.php @@ -0,0 +1,74 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Response; + +use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Pagerfanta; +use Pagerfanta\Exception\NotValidCurrentPageException; + +use Myddleware\RegleBundle\Classes\tools as MyddlewareTools; + +class AccountController extends Controller +{ + + /* ****************************************************** + * Account + ****************************************************** */ + public function displayAccountAction() { + $language = $this->container->getParameter('locale'); + return $this->render('RegleBundle:Account:index.html.twig', array( + "locale" => $language + )); + } + + public function changeLocaleAction() { + if(isset($_POST['locale'])) { + $locale = $_POST['locale']; + } else { + return Response("Something missing (parameter)"); + } + $tools = new MyddlewareTools($this->get('logger'), $this->container, $this->get('database_connection')); + if($locale == "fr") { + if($this->container->getParameter('locale') != "fr") { // Si la langue est déjà en Français ne rien faire, logique + $tools->changeMyddlewareParameter(array('locale'),'fr'); + } + } else { + if($this->container->getParameter('locale') != "en") { // Si la langue est déjà en Anglais ne rien faire, logique + $tools->changeMyddlewareParameter(array('locale'),'en'); + } + } + // Clear the cache to change the language + $process = new \Symfony\Component\Process\Process('php '. $this->container->get( 'kernel' )->getRootDir() .'/console cache:clear --env='. $this->container->get( 'kernel' )->getEnvironment()); + $process->run(); + return new Response("Success"); + } + +} diff --git a/src/Myddleware/RegleBundle/Controller/ConnectorController.php b/src/Myddleware/RegleBundle/Controller/ConnectorController.php new file mode 100644 index 000000000..34119f2ef --- /dev/null +++ b/src/Myddleware/RegleBundle/Controller/ConnectorController.php @@ -0,0 +1,796 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Controller; +//-- +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Response; +//-- +use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Pagerfanta; +use Pagerfanta\Exception\NotValidCurrentPageException; +//-- +use Myddleware\RegleBundle\Entity\Connector; +use Myddleware\RegleBundle\Entity\ConnectorParams; +//-- +use Myddleware\RegleBundle\Classes\tools; +use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; + +class ConnectorController extends Controller +{ + + /* ****************************************************** + * CONNECTOR + ****************************************************** */ + + // CALLBACK POUR LES APIS + public function callBackAction() { // REV 1.1.1 + try { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + // Nom de la solution + if(!isset($myddlewareSession['param']['myddleware']['connector']['solution']['callback'])) { + return new Response(''); + } + else { + $solution_name = $myddlewareSession['param']['myddleware']['connector']['solution']['callback']; + } + + $solution = $this->get('myddleware_rule.'.$solution_name); + + // ETAPE 2 : Récupération du retour de la Popup en GET et génération du token final + if(isset($_GET[$solution->nameFieldGet])) { + $solution->init($myddlewareSession['param']['connector']['source']); // Affecte les variables + + $solution->setAuthenticate($_GET[$solution->nameFieldGet]); + + if($solution->refresh_token) { // Si RefreshToken + $myddlewareSession['param']['connector']['source']['refreshToken'] = $solution->getRefreshToken(); + } + + // Save the session befor calling login function because session could be used in this function + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $solution->login( $myddlewareSession['param']['connector']['source'] ); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + // Sauvegarde des 2 jetons en session afin de les enregistrer dans les paramètres du connecteur + $myddlewareSession['param']['connector']['source']['token'] = $solution->getAccessToken(); + if($solution->refresh_token) { // Si RefreshToken + $myddlewareSession['param']['connector']['source']['refreshToken'] = $solution->getRefreshToken();; + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('connector_callback')); + } + + // SOLUTION AVEC POPUP --------------------------------------------------------------------- + // ATAPE 1 si la solution utilise un callback et le js + if( + $solution->callback + && $solution->js + ) { + if(!empty($myddlewareSession['param']['connector']['source'])) { + $params_connexion_solution = $myddlewareSession['param']['connector']['source']; + } + if(!empty($myddlewareSession['param']['connector']['source']['token'])) { + $params_connexion_solution['token'] = $myddlewareSession['param']['connector']['source']['token']; + } + if(!empty($myddlewareSession['param']['connector']['source']['refreshToken'])) { + $params_connexion_solution['refreshToken'] = $myddlewareSession['param']['connector']['source']['refreshToken']; + } + + $solution->init($params_connexion_solution); // Affecte les variables + + // Save the session befor calling login function because session could be used in this function + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $error = $solution->login( $params_connexion_solution ); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + // Gestion des erreurs retour méthode login + if(!empty($error)) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response(''); + } + + // Autorisation de l'application + if(!empty($_POST['solutionjs']) ) { + // Déclenche la pop up + if(!empty($_POST['detectjs'])) { + $callbackUrl = $solution->getCreateAuthUrl((isset($_SERVER['HTTPS']) ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$this->generateUrl('connector_callback')); + if(!empty($myddlewareSession['param']['connector']['source']['token'])) { + $solution->setAccessToken($myddlewareSession['param']['connector']['source']['token'] ); + } + // Redirection vers une autorisation manuel + else { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($solution->js.';'.urldecode($callbackUrl)); // Url de l'authentification prêt à être ouvert en popup + } + + // 1er test de validité du Token + $testToken = $solution->testToken(); + + if(!empty($testToken['error']['code'])) { + if($testToken['error']['code'] == 401 || $testToken['error']['code'] == 404) { + $myddlewareSession['param']['connector']['source']['token'] = NULL; + $url = $solution->getCreateAuthUrl($callbackUrl); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($solution->js.';'.urldecode($url)); // Url de l'authentification prêt à être ouvert en popup + } + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($solution->js.';'.$callbackUrl); // tentative de connexion + + } // detect js + + if(isset($myddlewareSession['param']['connector']['source']['token'])) { + $solution->setAccessToken($myddlewareSession['param']['connector']['source']['token'] ); + } + // 2nd Test la validité du token + $testToken = $solution->testToken(); + + // Erreur sans ouvrir la popup + if($testToken['error']['code'] == 404 || $testToken['error']['code'] === 0) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response("2;".$testToken['error']['message']); // Error Not Found + } + + if(isset($testToken['error']['code']) && !empty($testToken['error']['code']) && !empty($testToken['error']['message'])) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($testToken['error']['code'].';'.$testToken['error']['message']); + } + + if(isset($myddlewareSession['param']['connector']['source']['token'])) { + if(isset($testToken['error']['message']) && !empty($testToken['error']['message'])) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($testToken['error']['message'] . ';'); // Erreur de connexion + } + else { + $solution->connexion_valide = true; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response(1); // Connexion réussi + } + } + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response(''); // Ferme la popup automatiquement + } // fin + // SOLUTION AVEC POPUP --------------------------------------------------------------------- + else { + throw new \Exception('Failed load class'); + } + } + catch (\Exception $e) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($e->getMessage()); + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response(''); + } + + // Contrôle si le fichier upload est valide puis le déplace + public function uploadAction($solution) // REV 1.1.0 + { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + if( isset($solution) ) { + if(in_array(trim($solution), array('sagecrm','sapcrm','sap'))){ + $output_dir = __DIR__."/../Custom/Solutions/".trim($solution)."/wsdl/"; + // Get canonicalized absolute pathname + $path = realpath($output_dir); + // If it exist, check if it's a directory + if($path === false || !is_dir($path)) { + try { + if(!mkdir($output_dir,755,true)) { + echo '0;'.'Directory '.$output_dir.' doesn\'t exist. Failed to create this directory. Please check directory Custom is readable by webuser. You can create manually the directory for the Sage wsdl too. '; + exit; + } + } + catch (\Exception $e) { + echo '0;'.$e->getMessage().'. Please check you have the web user has the permission to write in the directory '.__DIR__.'/../Custom . '; + exit; + } + } + } + } + + + + // Supprime ancien fichier de config s'il existe + if(isset($_GET['file']) && $_GET['file'] != '') { + $name_without_space = str_replace(' ', '_', $_GET['file']); + $path_delete_old = $output_dir.$name_without_space; + if(file_exists($path_delete_old)) { + unlink( $path_delete_old ) ; + echo '

'.$this->get('translator')->trans('create_connector.upload_delete').' : '.htmlentities($name_without_space).'

'; + } + } + + if($solution == 'all') { + if(isset($myddlewareSession['param']['myddleware']['upload']['name'])) { + echo '1;'.$myddlewareSession['param']['myddleware']['upload']['name']; + unset($myddlewareSession['param']['myddleware']['upload']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + + if(isset($myddlewareSession['param']['myddleware']['upload']['error'])) { + echo '0;'.$myddlewareSession['param']['myddleware']['upload']['error']; + unset($myddlewareSession['param']['myddleware']['upload']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + } + + if(isset($_FILES['myfile']) && isset($output_dir) && is_dir($output_dir)) { + if ($_FILES['myfile']["error"] > 0) { + $error = $_FILES["file"]["error"]; + echo '0;'.$error; + $myddlewareSession['param']['myddleware']['upload']['error'] = $error; + } else { + // A list of permitted file extensions + $allowed = $this->container->getParameter('extension_allowed'); + $extension = pathinfo($_FILES['myfile']['name'], PATHINFO_EXTENSION); + + if(!in_array(strtolower($extension), $allowed)){ + echo '0;'.$this->get('translator')->trans('create_connector.upload_error_ext'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + + $name_without_space = str_replace(' ', '_', $_FILES['myfile']['name']); + $new_name = time().'_'.$name_without_space; + + if(move_uploaded_file($_FILES['myfile']['tmp_name'],$output_dir. $new_name)) { + echo "1;".$this->get('translator')->trans('create_connector.upload_success').' : '.$new_name; + $myddlewareSession['param']['myddleware']['upload']['name'] = $new_name; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + else { + echo '0;'.$this->get('translator')->trans('create_connector.upload_error'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + } else { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Connector:upload.html.twig',array( 'solution' => $solution ) + ); + } + + } // Rev 1.1.1 -------------------------- + + // CREATION D UN CONNECTEUR LISTE + public function createAction() + { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + $em = $this->getDoctrine()->getManager(); + $solution = $em->getRepository('RegleBundle:Solution') + ->solutionActive(); + $lstArray = array(); + if($solution) { + foreach ($solution as $s) { + $lstArray[$s->getName()] = ucfirst($s->getName()); + } + } + + $lst_solution = tools::composeListHtml($lstArray,$this->get('translator')->trans('create_rule.step1.list_empty')); + $myddlewareSession['param']['myddleware']['connector']['animation'] = false; + $myddlewareSession['param']['myddleware']['connector']['add']['message'] = 'list'; + // $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Connector:index.html.twig',array( + 'solutions'=> $lst_solution ) + ); + } + + // CREATION D UN CONNECTEUR + public function connectorInsertAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $type = ''; + + if($request->getMethod()=='POST' && isset($myddlewareSession['param']['connector'])) { + try { + // Récupère l'id d'une solution + $solution = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Solution') + ->findOneByName($myddlewareSession['param']['connector']['source']['solution']); + + $multi = $solution->getSource() + $solution->getTarget(); + + if(!empty($myddlewareSession['param']['myddleware']['connector']['animation'])) { + // animation add connector + $type = $myddlewareSession['param']['myddleware']['connector']['add']['type']; + // si la solution ajouté n'existe pas dans la page en cours on va la rajouter manuellement + if( !in_array($myddlewareSession['param']['connector']['source']['solution'], json_decode($myddlewareSession['param']['myddleware']['connector']['solution'][$type])) ) { + $myddlewareSession['param']['myddleware']['connector']['values'] = $type.';'.$myddlewareSession['param']['connector']['source']['solution'].';'.$multi.';'.$solution->getId(); + } + } + + // On récupére l'EntityManager + $em = $this->getDoctrine() + ->getManager(); + + // Création d'un connecteur + $unConnector = new Connector(); + $unConnector->setSolution( $solution ); + $unConnector->setLabel( $this->getRequest()->request->get('label') ); + $unConnector->setLabelSlug( $this->getRequest()->request->get('label') ); + $unConnector->setDateCreated(new \DateTime); + $unConnector->setDateModified(new \DateTime); + $unConnector->setCreatedBy( $this->getUser()->getId() ); + $unConnector->setModifiedBy( $this->getUser()->getId() ); + + $em->persist($unConnector); + $em->flush(); + + // Insert les paramètres de connexion du connecteur + foreach ( $myddlewareSession['param']['connector']['source'] as $connexion => $val ) { + if( $connexion != "solution" ) { + $unConnectoParams = new ConnectorParams(); + $unConnectoParams->setConnector( $unConnector->getId() ); + $unConnectoParams->setName( $connexion ); + $unConnectoParams->setValue( $val ); + $em->persist($unConnectoParams); + $em->flush(); + } + } + + unset($myddlewareSession['param']['connector']); + + if( + !empty($myddlewareSession['param']['myddleware']['connector']['add']['message']) + && $myddlewareSession['param']['myddleware']['connector']['add']['message'] == 'list' + ) { + unset($myddlewareSession['param']['myddleware']['connector']['add']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_connector_list')); + } + else { // animation + $message = ''; + if (!empty($myddlewareSession['param']['myddleware']['connector']['add']['message'])) { + $message = $myddlewareSession['param']['myddleware']['connector']['add']['message']; + } + unset($myddlewareSession['param']['myddleware']['connector']['add']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Connector:createout_valid.html.twig',array( + 'message' => $message, + 'type' => $type + ) + ); + } + + //----------- + } + catch(Exception $e) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + throw $this->createNotFoundException('Error'); + } + } + else { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + throw $this->createNotFoundException('Error'); + } + } + + // LISTE DES CONNECTEURS + public function connectorListAction($page) { + + try { + // --------------- + $em = $this->getDoctrine()->getManager(); + $compact['nb'] = 0; + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + // Detecte si la session est le support --------- + + $compact = $this->nav_pagination(array( + 'adapter_em_repository' => $em->getRepository('RegleBundle:Connector') + ->findListConnectorByUser( $permission->isAdmin($this->getUser()->getId()), $this->getUser()->getId() ), + 'maxPerPage' => $this->container->getParameter('pager'), + 'page' => $page + )); + + // Si tout se passe bien dans la pagination + if( $compact ) { + + // Si aucun connecteur + if( $compact['nb'] < 1 && !intval($compact['nb'])) { + $compact['entities'] = ''; + $compact['pager'] = ''; + } + + return $this->render('RegleBundle:Connector:list.html.twig',array( + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'] + ) + ); + } + else { + throw $this->createNotFoundException('Error'); + } + + // --------------- + }catch(Exception $e) { + throw $this->createNotFoundException('Error : '.$e); + } + } + + // SUPPRESSION DU CONNECTEUR + public function connectorDeleteAction($id) { + + if(isset($id)) { + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = array('id' => $id); + } + else { + $list_fields_sql = + array('id' => $id, + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + // Récupère le connecteur en fonction de son id + $connector = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Connector') + ->findOneBy( $list_fields_sql ); + + if($connector === null) { + return $this->redirect($this->generateUrl('regle_connector_list')); + } + + // On récupére l'EntityManager + $em = $this->getDoctrine() + ->getManager(); + + $connector_params = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $id ); + + if($connector_params) { + foreach ( $connector_params as $connector_param ) { + $em->remove($connector_param); + $em->flush(); + } + } + + $em->remove($connector); + $em->flush(); + + return $this->redirect($this->generateUrl('regle_connector_list')); + } + } + + // FICHE D UN CONNECTEUR + public function connectorOpenAction($id) { + $request = $this->get('request'); + + if($request->getMethod()=='POST') { + // SAVE + try { + // On récupére l'EntityManager + $em = $this->getDoctrine() + ->getManager(); + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = + array('id' => (int)$id + ); + } + else { + $list_fields_sql = + array( + 'id' => (int)$id, + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + // SAVE NOM CONNECTEUR + $connector = $em->getRepository('RegleBundle:Connector') + ->findBy( $list_fields_sql ); + + + + $connector[0]->setLabel( $_POST['nom'] ); + $em->persist($connector[0]); + $em->flush(); + + // SAVE PARAMS CONNECTEUR + if(count($_POST['params']) > 0) { + foreach($_POST['params'] as $p) { + $param = $em->getRepository('RegleBundle:ConnectorParams') + ->findOneBy( array( + 'id' => (int)$p['id'] + ) + ); + $param->setValue( $p['value'] ); + $em->persist($param); + $em->flush(); + } + return new Response($this->generateUrl('regle_connector_list')); + } + else { + return new Response(0); + } + + } + catch(Exception $e) { + return new Response($e->getMessage()); + } + // SAVE + } + else { + // FICHE + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = + array('id' => $id + ); + } + else { + $list_fields_sql = + array( + 'id' => $id, + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + + $em = $this->getDoctrine()->getManager(); + + // Infos du connecteur + $connectorP = $em->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $id ); + // Infos du connecteur + $connector = $em->getRepository('RegleBundle:Connector') + ->findBy( $list_fields_sql ); + + if(count($connector) == 0) { + return $this->redirect($this->generateUrl('regle_connector_list')); + } + + if( isset($connectorP) && count($connectorP > 0) ) { + $connector_params = array(); + $connector_params['label'] = $connector[0]->getLabel(); + + $connector_params['solution']['name'] = $connector[0]->getSolution()->getName(); + $connector_params['solution']['id'] = $connector[0]->getSolution()->getId(); + + foreach ($connectorP as $connectorObj) { + $connector_params['id'] = $connectorObj->getConnector(); + $connector_params['params'][$connectorObj->getName()]['value'] = $this->decrypt_params($connectorObj->getValue()); + $connector_params['params'][$connectorObj->getName()]['id'] = $connectorObj->getId(); + } + + } + + $solution = $this->get('myddleware_rule.'.$connector_params['solution']['name']); + + foreach ($solution->getFieldsLogin() as $k => $v) { + + $connector_params['params'][$v['name']]['type'] = $v['type']; + } + + return $this->render('RegleBundle:Connector:edit/fiche.html.twig',array( + 'connector_params' => $connector_params) + ); + } + + } + + /* ****************************************************** + * ANIMATION + ****************************************************** */ + // LISTE DES CONNECTEURS POUR ANIMATION + public function connectorListSolutionAction() { + + if(isset($_POST['id'])) { + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = array('solution' => (int)$_POST['id']); + } + else { + $list_fields_sql = + array('solution' => (int)$_POST['id'], + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + $em = $this->getDoctrine()->getManager(); + $listConnector = $em->getRepository('RegleBundle:Connector') + ->findBy( $list_fields_sql ); + + $lstArray = array(); + foreach ($listConnector as $c) { + $lstArray[$c->getId()] = ucfirst($c->getLabel()); + } + $lst = tools::composeListHtml($lstArray, $this->get('translator')->trans('create_rule.step1.choose_connector')); + return new Response($lst); + } + else { + return new Response(''); + } + } + + // CREATION D UN CONNECTEUR LISTE animation + public function createOutAction($type) + { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $em = $this->getDoctrine()->getManager(); + + $solution = $em->getRepository('RegleBundle:Solution') + ->solutionConnectorType($type); + + $lstArray = array(); + if($solution) { + foreach ($solution as $s) { + $lstArray[$s->getName()] = ucfirst($s->getName()); + } + } + + $lst_solution = tools::composeListHtml($lstArray,$this->get('translator')->trans('create_rule.step1.list_empty')); + $myddlewareSession['param']['myddleware']['connector']['add']['message'] = $this->get('translator')->trans('create_rule.step1.connector'); + $myddlewareSession['param']['myddleware']['connector']['add']['type'] = strip_tags($type); +Echo 'true
'; + $myddlewareSession['param']['myddleware']['connector']['animation'] = true; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Connector:createout.html.twig',array( + 'solutions'=> $lst_solution + ) + ); + } + + // RETOURNE LES INFOS POUR L AJOUT D UN CONNECTEUR EN JQUERY + public function connectorInsertSolutionAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + if(isset($myddlewareSession['param']['myddleware']['connector']['values'])) { + $values = $myddlewareSession['param']['myddleware']['connector']['values']; + unset($myddlewareSession['param']['myddleware']['connector']['values']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($values); + } + else { + return new Response(0); + } + } + + /* ****************************************************** + * METHODES PRATIQUES + ****************************************************** */ + + // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete + private function nav_pagination($params, $orm = true) { + + /* + * adapter_em_repository = requete + * maxPerPage = integer + * page = page en cours + */ + + if(is_array($params)) { + $compact = array(); + + #On passe l’adapter au bundle qui va s’occuper de la pagination + if($orm) { + $compact['pager'] = new Pagerfanta( new DoctrineORMAdapter($params['adapter_em_repository']) ); + } + else { + $compact['pager'] = new Pagerfanta( new ArrayAdapter($params['adapter_em_repository']) ); + } + + #On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) + $compact['pager']->setMaxPerPage($params['maxPerPage']); + try { + $compact['entities'] = $compact['pager'] + #On indique au pager quelle page on veut + ->setCurrentPage($params['page']) + #On récupère les résultats correspondant + ->getCurrentPageResults(); + + $compact['nb'] = $compact['pager']->getNbResults(); + + } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { + #Si jamais la page n’existe pas on léve une 404 + throw $this->createNotFoundException("Cette page n'existe pas."); + } + + return $compact; + } + else { + return false; + } + } + + + // Décrypte les paramètres de connexion d'une solution + private function decrypt_params($tab_params) { + if( is_array($tab_params) ) { + $return_params = array(); + foreach ($tab_params as $key => $value) { + if(is_string($value)) { + $return_params[$key] = $value; + } + } + return $return_params; + } + else { + return $tab_params; + } + } + + +} diff --git a/src/Myddleware/RegleBundle/Controller/DefaultController.php b/src/Myddleware/RegleBundle/Controller/DefaultController.php new file mode 100644 index 000000000..6b6160a25 --- /dev/null +++ b/src/Myddleware/RegleBundle/Controller/DefaultController.php @@ -0,0 +1,2868 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Response; + +use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Pagerfanta; +use Pagerfanta\Exception\NotValidCurrentPageException; + +use Myddleware\RegleBundle\Entity\Solution; +use Myddleware\RegleBundle\Entity\Connector; +use Myddleware\RegleBundle\Entity\ConnectorParams; +use Myddleware\RegleBundle\Entity\Rule; +use Myddleware\RegleBundle\Entity\RuleParams; +use Myddleware\RegleBundle\Entity\RuleFilters; +use Myddleware\RegleBundle\Entity\RuleFields; +use Myddleware\RegleBundle\Entity\RuleRelationShips; +use Myddleware\RegleBundle\Entity\Functions; +use Myddleware\RegleBundle\Entity\FunctionsRelationShips; +use Myddleware\RegleBundle\Entity\FuncCat; +use Myddleware\RegleBundle\Entity\FuncCatRelationShips; + +use Myddleware\RegleBundle\Classes\rule as RuleClass; +use Myddleware\RegleBundle\Classes\document; +use Myddleware\RegleBundle\Classes\tools; + +class DefaultControllerCore extends Controller +{ + // Connexion bdd doctrine + protected $em; + // Connexion direct bdd (utilisé pour créer les tables Z sans doctrine + protected $connection; + + protected function getInstanceBdd() { + if (empty($this->em)) { + $this->em = $this->getDoctrine()->getManager(); + } + if (empty($this->connection)) { + $this->connection = $this->get('database_connection'); + } + } + // debug ladybug_dump($compact); + + /* ****************************************************** + * RULE + ****************************************************** */ + + // LISTE DES REGLES + public function ruleListAction($page) { + try { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + if(isset($myddlewareSession['rule']['newid'])) { + $id = $myddlewareSession['rule']['newid']; + unset($myddlewareSession['rule']['newid']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_open', array('id'=>$id))); + } + + $this->getInstanceBdd(); + $compact['nb'] = 0; + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + // Detecte si la session est le support --------- + + $compact = $this->nav_pagination(array( + 'adapter_em_repository' => $this->em->getRepository('RegleBundle:Rule')->findListRuleByUser( $permission->isAdmin($this->getUser()->getId()),$this->getUser()->getId() ), + 'maxPerPage' => $this->container->getParameter('pager'), + 'page' => $page + )); + + // Si tout se passe bien dans la pagination + if( $compact ) { + + // Si aucune règle + if( $compact['nb'] < 1 && !intval($compact['nb'])) { + $compact['entities'] = ""; + $compact['pager'] = ""; + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Rule:list.html.twig',array( + 'nb_rule' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'] + ) + ); + } + else { + throw $this->createNotFoundException('Error'); + } + + // --------------- + }catch(Exception $e) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + throw $this->createNotFoundException('Error : '.$e); + } + + + } + + // SUPPRESSION D UNE REGLE + public function ruleDeleteAction($id) { + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = + array('id' => $id + ); + } + else { + $list_fields_sql = + array( + 'id' => $id, + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + if(isset($id)) { + // Récupère la règle en fonction de son id + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findBy( $list_fields_sql); + + $rule = $rule[0]; + + // si je supprime une règle qui ne m'appartient pas alors redirection + if(count($rule) < 1) { + return $this->redirect($this->generateUrl('regle_list')); + } + + $doc = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Documents') + ->findOneBy( array( + 'rule' => $id + ) + ); + + // On récupére l'EntityManager + $this->getInstanceBdd(); + + // si aucun document (flux) alors on supprime sinon on flag + if(is_null($doc)) { + + $rule_params = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleParams') + ->findByRule( $id ); + + $rule_fields = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleFields') + ->findByRule( $id ); + + if($rule_params) { + foreach ( $rule_params as $rule_param ) { + $this->em->remove($rule_param); + $this->em->flush(); + } + } + + if($rule_fields) { + foreach ( $rule_fields as $rule_field ) { + $this->em->remove($rule_field); + $this->em->flush(); + } + } + + $rule_filters = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleFilters') + ->findByRule( $id ); + + if($rule_filters) { + foreach ( $rule_filters as $rule_filter ) { + $this->em->remove($rule_filter); + $this->em->flush(); + } + } + + // - - - + $this->connection->executeQuery( 'DROP TABLE IF EXISTS z_'.$rule->getNameSlug().'_'.$rule->getVersion().'_source' ); + $this->connection->executeQuery( 'DROP TABLE IF EXISTS z_'.$rule->getNameSlug().'_'.$rule->getVersion().'_target' ); + $this->connection->executeQuery( 'DROP TABLE IF EXISTS z_'.$rule->getNameSlug().'_'.$rule->getVersion().'_history' ); + + $stmt = $this->connection->prepare('DELETE FROM RuleRelationShips WHERE rule_id =:id'); + $stmt->bindValue('id', $rule->getId() ); + $stmt->execute(); + + // - - - + + $this->em->remove($rule); + $this->em->flush(); + } + else { // flag + + $rule->setDeleted( 1 ); + $rule->setActive( 0 ); + $this->em->persist($rule); + $this->em->flush(); + } + + return $this->redirect($this->generateUrl('regle_list')); + } + } + + // AFFICHE LES FLUX D'UNE REGLE + public function displayFluxAction($id){ + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findOneBy( array( + 'id' => $id + ) + ); + + $myddlewareSession['flux_filter']['where'] = "WHERE rule_id = '" . $rule->getId() . "'"; + $myddlewareSession['flux_filter']['c']['rule'] = $rule->getId(); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('flux_list')); + } + + // ACTIVE UNE REGLE + public function ruleUpdActiveAction($id) { + + try { + + // On récupére l'EntityManager + $this->getInstanceBdd(); + + + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findOneById( $id ); + + if($rule->getActive()) { + $r = 0; + $rule->setActive( $r ); + } + else { + $r = 1; + $rule->setActive( $r ); + } + + $this->em->persist($rule); + $this->em->flush(); + + return new Response($r); + } + catch(Exception $e) { + echo $e->getMessage(); + } + } + + // Executer une règle manuellement + public function ruleExecAction($id) { + try { + if($id == "ALL") { + $rule = $this->container->get('myddleware_rule.rule'); + $result = $rule->actionRule("ALL"); + return $this->redirect($this->generateUrl('regle_list')); + } else if ($id == "ERROR") { + $rule = $this->container->get('myddleware_rule.rule'); + $result = $rule->actionRule("ERROR"); + return $this->redirect($this->generateUrl('regle_list')); + } else { + $rule = $this->container->get('myddleware_rule.rule'); + $rule->setRule($id); + $result = $rule->actionRule('runMyddlewareJob'); + + return $this->redirect( $this->generateURL('regle_open', array( 'id'=>$id )) ); + exit; + } + } + catch(Exception $e) { + return $this->redirect($this->generateUrl('regle_list')); + } + } + + // MODIFIE LES PARAMETRES D UNE REGLE + public function ruleUpdParamsAction($id) { + + try { + // On récupére l'EntityManager + $this->getInstanceBdd(); + + if(isset($_POST['params']) && is_array($_POST['params'])) { + foreach($_POST['params'] as $p) { + $param = $this->em->getRepository('RegleBundle:RuleParams') + ->findOneBy( array( + 'rule' => $id, + 'id' => (int)$p['id'] + ) + ); + + $param->setValue( $p['value'] ); + $this->em->persist($param); + $this->em->flush(); + } + } + + return new Response(1); + } + catch(Exception $e) { + echo $e->getMessage(); + } + } + + // SIMULE LA LECTURE POUR RETOURNER LE NOMBRE DE TRANSFERS POTENTIELS + public function ruleSimulateTransfersAction($id) { + try { + // On récupére l'EntityManager + $this->getInstanceBdd(); + + // Récupération de date_ref + $param['date_ref'] = $this->em->getRepository('RegleBundle:RuleParams') + ->findOneBy( array( + 'rule' => $id, + 'name' => 'datereference' + )) + ->getValue(); + + // Infos Rule + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findOneById( $id ); + + // Id Connecteur + $connectorSourceId = (string)$rule->getConnectorSource()->getId(); + + // Champs + $fields = $this->em->getRepository('RegleBundle:RuleFields') + ->findByRule( $id ); + + $param['fields'] = array(); + // Extraction des champs sources + + foreach ($fields as $obj) { + // It could be several fields in a source when there is a formula + $sources = explode(';',$obj->getSource()); + foreach ($sources as $source) { + $param['fields'][] = $source; + } + } + + // Module source + $param['module'] = (string)$rule->getModuleSource(); + + // Solution source + $solution_source_nom = $rule->getConnectorSource()->getSolution()->getName(); + + // Connector source ------------------- + $connectorParamsSource = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $rule->getConnectorSource() ); + + $connectorSource['solution'] = $rule->getConnectorSource()->getSolution()->getName(); + + foreach ($connectorParamsSource as $connector) { + $connectorSource[ $connector->getName() ] = $connector->getValue(); + } + + $solution_source = $this->get('myddleware_rule.'.$solution_source_nom); + + $solution_source->login($connectorSource); + + // Rule Mode + $param['rule']['rule_mode'] = $this->em->getRepository('RegleBundle:RuleParams') + ->findOneBy( array( + 'rule' => $id, + 'name' => 'mode' + )) + ->getValue(); + + if(empty($param['rule']['rule_mode'])) { + $param['rule']['rule_mode'] = "0"; + } + $param['offset'] = '0'; + $result = $solution_source->read($param); + + if(!empty($result['error'])) { + throw new \Exception("Reading Issue: ".$result['error']); + } + if(isset($result['count'])) { + return new Response($result['count']); + } + return new Response(0); + } + catch(\Exception $e) { + return new Response( json_encode(array('error' => $e->getMessage())) ); + } + } + + // MODE EDITION D UNE REGLE + public function ruleEditAction($id) { + try { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + if(isset($id)) { + //-- + // si une session existe alors on la supprime + if(isset($myddlewareSession['param']['rule'])) { + unset( $myddlewareSession['param']['rule'] ); + } + + // préparation des sessions + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findOneById( $id ); + + // composition des sessions + $myddlewareSession['param']['rule']['rulename_valide'] = true; + $myddlewareSession['param']['rule']['rulename'] = $rule->getName(); + $myddlewareSession['param']['rule']['connector']['source'] = (string)$rule->getConnectorSource()->getId(); + $myddlewareSession['param']['rule']['connector']['cible'] = (string)$rule->getConnectorTarget()->getId(); + + $version = (int)$rule->getVersion(); + $version++; + if(strlen($version) == 1) { + $version = "00".$version; + } + else if(strlen($version) == 2) { + $version = "0".$version; + } + + $myddlewareSession['param']['rule']['version'] = $version; + $myddlewareSession['param']['rule']['last_version_id'] = $rule->getId(); + + // Connector source ------------------- + $connectorParamsSource = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $rule->getConnectorSource() ); + + $myddlewareSession['param']['rule']['source']['solution'] = $rule->getConnectorSource()->getSolution()->getName(); + + foreach ($connectorParamsSource as $connector) { + $myddlewareSession['param']['rule']['source'][ $connector->getName() ] = $connector->getValue(); + } + // Connector source ------------------- + + // Connector target ------------------- + $connectorParamsTarget = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $rule->getConnectorTarget() ); + + $myddlewareSession['param']['rule']['cible']['solution'] = $rule->getConnectorTarget()->getSolution()->getName(); + + foreach ($connectorParamsTarget as $connector) { + $myddlewareSession['param']['rule']['cible'][ $connector->getName() ] = $connector->getValue(); + } + // Connector target ------------------- + + $ruleParams = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleParams') + ->findByRule( $rule->getId() ); + // Paramètre d'une règle + if($ruleParams) { + foreach ($ruleParams as $ruleParamsObj) { + // récupération des submodules + if($ruleParamsObj->getName() == 'structure') { + $myddlewareSession['param']['rule']['source']['structure'] = json_decode($ruleParamsObj->getValue(),true); + } + else { + $params[] = array( + 'name' => $ruleParamsObj->getName(), + 'value' => $ruleParamsObj->getValue() + ); + } + } + $myddlewareSession['param']['rule']['reload']['params'] = json_encode($params); + } + + // Modules -- + $myddlewareSession['param']['rule']['source']['module'] = $rule->getModuleSource(); + $myddlewareSession['param']['rule']['cible']['module'] = $rule->getModuletarget(); + // Modules -- + + // reload --------------- + $ruleFields = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleFields') + ->findByRule( $rule->getId() ); + // get_modules_fields en source pour avoir l'association fieldid / libellé (ticket 548) + $solution_source_nom = $myddlewareSession['param']['rule']['source']['solution']; + $solution_source = $this->get('myddleware_rule.'.$solution_source_nom); + // $solution_source->login($this->decrypt_params($myddlewareSession['param']['rule']['source'])); + $solution_source->login($myddlewareSession['param']['rule']['source']); + + // SOURCE ----- Récupère la liste des champs source + // O récupère le module de la règle et les sous modules s'il y en a + $sourceModule = $rule->getModuleSource(); + if( $solution_source->get_submodules($rule->getModuleSource(),'source') != null ) { + if(isset($myddlewareSession['param']['rule']['source']['structure'])) { + $sourceModule = array($sourceModule => $myddlewareSession['param']['rule']['source']['structure']); + } + } + $sourceFieldsInfo = $solution_source->get_module_fields($sourceModule); + + // Champs et formules d'une règle + if($ruleFields) { + foreach ($ruleFields as $ruleFieldsObj) { + $array = array( + 'target' => $ruleFieldsObj->getTarget(), + 'source' => array(), + 'formula' => $ruleFieldsObj->getFormula() + ); + $fields_source = explode(';', $ruleFieldsObj->getSource()); + + if (!empty($fields_source)) { + foreach ($fields_source as $field_source) { + if($field_source == "my_value") { + $array['source'][$field_source] = "my_value"; + } elseif(isset($sourceFieldsInfo[$field_source])) { + $array['source'][$field_source] = $sourceFieldsInfo[$field_source]['label']; + } else { + if (!empty($sourceFieldsInfo)) { + foreach ($sourceFieldsInfo as $multiModule) { + if(isset($multiModule[$field_source])) { + $array['source'][$field_source] = $multiModule[$field_source]['label']; + } + } + } + } + if(!isset($array['source'][$field_source])) { + throw new \Exception ("Getting label (reload): Field ".$field_source." not found."); + } + } + $fields[] = $array; + } + } + $myddlewareSession['param']['rule']['reload']['fields'] = json_encode($fields); + } + + $ruleRelationShips = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleRelationShips') + ->findByRule( $rule->getId() ); + // Relations d'une règle + if($ruleRelationShips) { + foreach ($ruleRelationShips as $ruleRelationShipsObj) { + $relate[] = array( + 'source' => $ruleRelationShipsObj->getFieldNameSource(), + 'target' => $ruleRelationShipsObj->getFieldNameTarget(), + 'id' => $ruleRelationShipsObj->getFieldId() + ); + } + + $myddlewareSession['param']['rule']['reload']['relate'] = json_encode($relate); + } + + + // Filter + $ruleFilters = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleFilters') + ->findByRule( $rule->getId() ); + + if($ruleFilters) { + foreach ($ruleFilters as $ruleFiltersObj) { + $filter[] = array( + 'target' => $ruleFiltersObj->getTarget(), + 'type' => $ruleFiltersObj->getType(), + 'value' => $ruleFiltersObj->getValue() + ); + } + } + + $myddlewareSession['param']['rule']['reload']['filter'] = ((isset($filter)) ? json_encode($filter) : json_encode(array()) ); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // reload --------------- + return $this->redirect($this->generateUrl('regle_stepthree')); + exit; + //-- + } + } + catch(Exception $e) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.update'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + } + + // FICHE D UNE REGLE + public function ruleOpenAction($id) { + + $this->getInstanceBdd(); + $tools = new tools($this->get('logger'), $this->container, $this->connection); + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = array('id' => $id); + } + else { + $list_fields_sql = + array('id' => $id, + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + // Infos de la regle + $rule = $this->em->getRepository('RegleBundle:Rule') + ->findOneBy( $list_fields_sql ); + + if(!$rule) { + throw $this->createNotFoundException('La fiche n existe pas dans la base de donnees'); + } + + // Liste des relations + $rule_relationships = $this->em->getRepository('RegleBundle:RuleRelationShips') + ->findByRule( $rule->getId() ); + + //NADA + $solution_cible_nom = $rule->getConnectorTarget()->getSolution()->getName(); + $solution_cible= $this->get('myddleware_rule.'.$solution_cible_nom); + $moduleCible = (string)$rule->getModuleTarget(); + // Champs pour éviter les doublons + $fieldsDuplicateTarget = $solution_cible->getFieldsDuplicate($moduleCible); + // Les champs sélectionnés + $duplicate_fields = $this->em->getRepository('RegleBundle:RuleParams') + ->findOneBy( array( + 'rule' => $id, + 'name' => 'duplicate_fields' + )); + + if (isset($duplicate_fields)) + $duplicate_fields = $duplicate_fields->getValue(); + //NADA FIN + + $tab_rs = array(); + $i = 0; + foreach ($rule_relationships as $r) { + + $tab_rs[$i]['getFieldId'] = $r->getFieldId(); + $tab_rs[$i]['getFieldNameSource'] = $r->getFieldNameSource(); + $tab_rs[$i]['getFieldNameTarget'] = $r->getFieldNameTarget(); + + $ruleTmp = $this->em->getRepository('RegleBundle:Rule') + ->findOneBy( array( + 'id' => $r->getFieldId() + ) + ); + + $tab_rs[$i]['getName'] = $ruleTmp->getName(); + $i++; + } + + // Infos connecteurs & solutions + $connector = $this->em->getRepository('RegleBundle:Rule') + ->infosConnectorByRule( $rule->getId() ); + + // Changement de référence pour certaines solutions + $autorization_source = $connector[0]['solution_source']; + $autorization_module_trans = mb_strtolower( $rule->getModuleSource() ); + + // Infos params + $Params = $this->em->getRepository('RegleBundle:RuleParams') + ->findByRule( $rule->getId() ); + + // Infos champs + $Fields = $this->em->getRepository('RegleBundle:RuleFields') + ->findByRule( $rule->getId() ); + + // Infos champs + $Filters = $this->em->getRepository('RegleBundle:RuleFilters') + ->findByRule( $rule->getId() ); + + $ruleParam = RuleClass::getFieldsParamView(); + $params_suite = false; + if($Params) { + foreach ($Params as $field) { + foreach ($ruleParam as $index => $value) { + if($field->getName() == $value['name']) { + $ruleParam[$index]['id_bdd'] = $field->getId(); + $ruleParam[$index]['value_bdd'] = $field->getValue(); + if($field->getName() == 'datereference') { + // Test si la date est valide + $date = $field->getValue(); + $format = 'Y-m-d H:i:s'; + $d = \DateTime::createFromFormat($format, $date); + $r = $d && $d->format($format) == $date; + + if(!$r) { + $ruleParam[$index]['id'] = 'datereference_txt'; + $ruleParam[$index]['name'] = 'datereference_txt'; + $ruleParam[$index]['label'] = $autorization_source.'.'.$autorization_module_trans.'.ref'; + } + } + } + } + + if($field->getName() == 'mode') { + // We send the translation of the mode to the view + switch ($field->getValue()) { + case '0': + $params_suite['mode'] = $tools->getTranslation(array('create_rule', 'step3', 'syncdata', 'create_modify')); + break; + case 'C': + $params_suite['mode'] = $tools->getTranslation(array('create_rule', 'step3', 'syncdata', 'create_only')); + break; + case 'S': + $params_suite['mode'] = $tools->getTranslation(array('create_rule', 'step3', 'syncdata', 'search_only'));; + break; + default: + $params_suite['mode'] = $field->getValue(); + } + } + + if($field->getName() == 'bidirectional') { + $ruleBidirectional = $this->em->getRepository('RegleBundle:Rule') + ->findOneBy( array( + 'id' => $field->getValue() + ) + ); + // Send the name and the id of the opposite rule to the view + $params_suite['bidirectional'] = $field->getValue(); + $params_suite['bidirectionalName'] = $ruleBidirectional->getName(); + } + + } + } + + return $this->render('RegleBundle:Rule:edit/fiche.html.twig',array( + 'rule' => $rule, + 'connector' => $connector[0], + 'fields' => $Fields, + 'relate' => $tab_rs, + 'params' => $ruleParam, + 'filters' => $Filters, + 'params_suite' => $params_suite + ) + ); + } + + // CREATION - STEP ONE - CONNEXION : jQuery ajax + public function ruleInputsAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + if($request->getMethod()=='POST') { + + // Retourne la liste des inputs pour la connexion + if($this->getRequest()->request->get('mod') == 1) { + + if(is_string($this->getRequest()->request->get('solution')) && is_string($this->getRequest()->request->get('parent'))) { + if(preg_match("#[\w]#", $this->getRequest()->request->get('solution')) && preg_match("#[\w]#", $this->getRequest()->request->get('parent'))) + { + $classe = strtolower($this->getRequest()->request->get('solution')); + + $solution = $this->get('myddleware_rule.'.$classe); + $parent = $this->getRequest()->request->get('parent'); + $liste_input = $solution->getFieldsLogin(); + + $this->getInstanceBdd(); + $solution = $this->em->getRepository('RegleBundle:Solution') + ->findByName($classe); + $solution = $solution[0]; + + $contenu = '

'; + + if($solution->getSource()) { + $contenu .= ' '.$this->get('translator')->trans('create_connector.source'); + } + + if($solution->getTarget()) { + $contenu .= ' '.$this->get('translator')->trans('create_connector.target'); + } + + $contenu .= "

"; + + if($liste_input) { + foreach ($liste_input as $input) { + // Rev 1.1.0 ----- + if($input['name'] == 'wsdl') { + $contenu .= '

+ +

'; + } + else { + $contenu .= '

+

'; + } + // Rev 1.1.0 ----- + } + } + + return $this->render('RegleBundle:Ajax:result_liste_inputs.html.twig',array( + 'contenu'=>$contenu, + 'parent'=>$parent) + ); + } + } + } // Vérifie si la connexion peut se faire ou non + elseif($this->getRequest()->request->get('mod') == 2 || $this->getRequest()->request->get('mod') == 3) { + + // Connector + if($this->getRequest()->request->get('mod') == 2) { + + if(preg_match("#[\w]#", $this->getRequest()->request->get('champs')) && preg_match("#[\w]#", $this->getRequest()->request->get('parent')) && preg_match("#[\w]#", $this->getRequest()->request->get('solution'))) { + + $classe = strtolower($this->getRequest()->request->get('solution')); + $solution = $this->get('myddleware_rule.'.$classe); + + // établi un tableau params + $champs = explode(';',$this->getRequest()->request->get('champs')); + + if($champs) { + foreach ($champs as $key) { + $input = explode('::',$key); + if(!empty($input[0])) { + if(!empty($input[1]) || is_numeric($input[1])) { + $param[$input[0]] = trim($input[1]); + $myddlewareSession['param']['connector'][$this->getRequest()->request->get('parent')][$input[0]] = trim($input[1]); + } + } + } + } + + $myddlewareSession['param']['connector'][$this->getRequest()->request->get('parent')]['solution'] = $classe; + + // Vérification du nombre de champs + if( isset($param) && (count($param) == count($solution->getFieldsLogin())) ) { + // Save the session befor calling login function because session could be used in this function + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $solution->login($param); + $r = $solution->connexion_valide; + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + if(!empty($r)) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo "1"; // Connexion valide + exit; + } + else { + unset($myddlewareSession['param']['rule']); + echo "0"; // Erreur de connexion + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + } + else { + echo "0"; // Erreur pas le même nombre de champs + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + } + } // Rule + elseif($this->getRequest()->request->get('mod') == 3) { + + // 0 : solution + // 1 : id connector + $params = explode('_',$this->getRequest()->request->get('solution')); + + // Deux params obligatoires + if(count($params) == 2 && intval($params[1]) && is_string($params[0])) { + unset($myddlewareSession['param']['rule'][$this->getRequest()->request->get('parent')]); + $classe = strtolower($params[0]); + $solution = $this->get('myddleware_rule.'.$classe); + + $connector = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Connector') + ->findOneById( $params[1] ); + + $connector_params = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $connector ); + + if($connector_params) { + foreach ($connector_params as $key) { + $myddlewareSession['param']['rule'][$this->getRequest()->request->get('parent')][$key->getName()] = $key->getValue(); + } + } + + $myddlewareSession['param']['rule']['rulename'] = $this->getRequest()->request->get('name'); + + // Affectation id connector + $myddlewareSession['param']['rule']['connector'][$this->getRequest()->request->get('parent')] = $params[1]; + //$myddlewareSession['obj'][$this->getRequest()->request->get('parent')] = $connector_params; + + // $solution->login($this->decrypt_params($myddlewareSession['param']['rule'][$this->getRequest()->request->get('parent')])); + $solution->login($myddlewareSession['param']['rule'][$this->getRequest()->request->get('parent')]); + $myddlewareSession['param']['rule'][$this->getRequest()->request->get('parent')]['solution'] = $classe; + + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $r = $solution->connexion_valide; + if(!empty($r)) { + echo "1"; // Connexion valide + exit; + } + else { + echo "0"; // Erreur de connexion + exit; + } + + exit; + + return $this->render('RegleBundle:Ajax:result_connexion.html.twig',array() + ); + } + else { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo "0"; + exit; + } + } + } + } + else { + throw $this->createNotFoundException('Error'); + } + } + + // CREATION - STEP ONE - CONNEXION + public function ruleStepOneAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // s'il existe des vielles données on les supprime + if(isset( $myddlewareSession['param']['rule'] )) { + unset( $myddlewareSession['param']['rule'] ); + } + + // Détecte s'il existe des erreurs + if(isset($myddlewareSession['error']['create_rule']) && !empty($myddlewareSession['error']['create_rule']) ) { + $error = $myddlewareSession['error']['create_rule']; + unset($myddlewareSession['error']['create_rule']); + } + else { + $error = false; + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Rule:create/step1.html.twig',array( + 'solutions_source'=>$this->liste_connectorAction('source'), + 'solutions_target'=>$this->liste_connectorAction('target'), + 'error' => $error + ) + ); + } + + // CREATION - STEP ONE - VERIF ALIAS RULE + public function ruleNameUniqAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + if($request->getMethod()=='POST') { + + $this->getInstanceBdd(); + + // Cherche si la règle existe en fonction de son nom + $rule = $this->em->getRepository('RegleBundle:Rule') + ->findOneBy( array( + 'name' => $this->getRequest()->request->get('name') + ) + ); + + // 0 existe pas 1 existe + if($rule == NULL) { + echo 0; + $myddlewareSession['param']['rule']['rulename_valide'] = true; + $myddlewareSession['param']['rule']['rulename'] = $this->getRequest()->request->get('name'); + } + else { + echo 1; + $myddlewareSession['param']['rule']['rulename_valide'] = false; + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + else { + throw $this->createNotFoundException('Error'); + } + } + + // CREATION - STEP TWO - CHOIX MODULES + public function ruleStepTwoAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // si le nom de la règle est inferieur à 3 caractères : + if(!isset($myddlewareSession['param']['rule']['source']['solution']) || strlen($myddlewareSession['param']['rule']['rulename']) < 3 || $myddlewareSession['param']['rule']['rulename_valide'] == false) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.valid'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + try{ + // ---------------- SOURCE ---------------------------- + $solution_source_nom = $myddlewareSession['param']['rule']['source']['solution']; + $solution_source = $this->get('myddleware_rule.'.$solution_source_nom); + + // $solution_source->login($this->decrypt_params($myddlewareSession['param']['rule']['source'])); + $solution_source->login($myddlewareSession['param']['rule']['source']); + + if(empty($solution_source->connexion_valide)) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.source_module_connect'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + $liste_modules_source = tools::composeListHtml($solution_source->get_modules('source'), $this->get('translator')->trans('create_rule.step2.choice_module')); + if(!$liste_modules_source) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.source_module_load_list'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + // ---------------- /SOURCE ---------------------------- + } + catch(Exception $e) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.source_module_all'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + try{ + // ---------------- TARGET ---------------------------- + // Si la solution est la même que la précèdente on récupère les infos + if($myddlewareSession['param']['rule']['source']['solution'] == $myddlewareSession['param']['rule']['cible']['solution']) { + $solution_cible = $solution_source; + $solution_cible_nom = $solution_source_nom; + } + else { + $solution_cible_nom = $myddlewareSession['param']['rule']['cible']['solution']; + $solution_cible = $this->get('myddleware_rule.'.$solution_cible_nom); + } + // $solution_cible->login($this->decrypt_params($myddlewareSession['param']['rule']['cible'])); + $solution_cible->login($myddlewareSession['param']['rule']['cible']); + + if(empty($solution_cible->connexion_valide)) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.target_module_connect'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + $liste_modules_cible = tools::composeListHtml( $solution_cible->get_modules('target'), $this->get('translator')->trans('create_rule.step2.choice_module')); + + if(!$liste_modules_cible) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.target_module_load_list'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + // ---------------- /TARGET ---------------------------- + } + catch(Exception $e) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.target_module_all'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + return $this->render('RegleBundle:Rule:create/step2.html.twig',array( + 'solution_source'=>$solution_source_nom, + 'solution_cible'=>$solution_cible_nom, + 'liste_modules_source'=>$liste_modules_source, + 'liste_modules_cible'=>$liste_modules_cible, + 'params' => $myddlewareSession['param']['rule'] + ) + ); + + } + + // CREATION - STEP THREE - SIMULATION DES DONNEES + public function ruleSimulationAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + if( $request->getMethod()=='POST' && isset( $myddlewareSession['param']['rule'] )) { + + // retourne un tableau prêt à l'emploi + $target = $this->createListeParamsRule( + $this->getRequest()->request->get('champs'), // Fields + $this->getRequest()->request->get('formules'), // Formula + '' // Params flux + ); + + + $solution_source_nom = $myddlewareSession['param']['rule']['source']['solution']; + $solution_source = $this->get('myddleware_rule.'.$solution_source_nom); + $solution_source->login($myddlewareSession['param']['rule']['source']); + $doc = $this->get('myddleware.document'); + $tab_simulation = array(); + $sourcesfields = array(); + + // récupération de tous les champs + if(isset($target['fields']) && count($target['fields']) > 0) { + foreach($target['fields'] as $f) { + if(isset($f)) { + foreach($f as $name_fields_target => $k ) { + + if(isset($k['champs'])) { + $sourcesfields = array_merge($k['champs'],$sourcesfields); + } + else { + $sourcesfields = $sourcesfields; + } + + } + } + + } + } + else { + // ici pour les règles avec des relations uniquement + return $this->render('RegleBundle:Rule:create/onglets/simulation_tab.html.twig',array( + 'before' => array(), // source + 'after' => array(), // target + 'data_source' => false + ) + ); + } + + // Récupère données source + $source = $solution_source->read_last( array( + 'module' => $myddlewareSession['param']['rule']['source']['module'], + 'fields' => $sourcesfields)); + + if( isset($source['done']) ) { + $before = array(); + $after = array(); + if($source['done']) { + foreach($target['fields'] as $f) { + foreach($f as $name_fields_target => $k ) { + $r['after'] = array(); + // Préparation pour transformation + $name = trim($name_fields_target); + $target_fields = array( + 'rulef_target_field_name' => $name, + 'rulef_source_field_name' => ((isset($k['champs'])) ? implode(';',$k['champs']) : 'my_value' ), + 'rulef_formula' => ((isset($k['formule'][0]) ? $k['formule'][0] : '' )), + 'rulef_related_rule' => '' + ); + + + + + // Transformation + $r['after'][$name_fields_target] = $doc->getTransformValue( $source['values'] , $target_fields ); + + if(empty($k['champs'])) + $k['fields']['Formula'] = ((isset($k['formule'][0]) ? $k['formule'][0] : '' )); + else if(count($k['champs']) > 0) { + foreach ($k['champs'] as $fields) { + // Fields couldn't be return. For example Magento return only field not empty + if (!empty($source['values'][ $fields ])) { + $k['fields'][ $fields ] = $source['values'][ $fields ]; + } + else { + $k['fields'][ $fields ] = ''; + } + } + } + else { + $k['fields'] = array(); + } + + $tab_simulation[] = array( + 'after' => $r['after'], + 'before' => $k['fields'] + ); + + } + } + $after = array(); + // Préparation pour tableau template + foreach ($tab_simulation as $key => $value) { + foreach($value as $k => $v) { + if($k == 'before') { + $before[] = $v; + } + else { + $after[] = $v; + } + } + } + } + } + return $this->render('RegleBundle:Rule:create/onglets/simulation_tab.html.twig',array( + 'before' => $before, // source + 'after' => $after, // target + 'data_source' => $source['done'], + 'params' => $myddlewareSession['param']['rule'] + ) + ); + + } + else { + throw $this->createNotFoundException('Error'); + } + + } + + // CREATION - STEP THREE - CHOIX DES CHAMPS - MAPPING DES CHAMPS + public function ruleStepThreeAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $this->getInstanceBdd(); + + // Test que l'ordre des étapes + if(!isset($myddlewareSession['param']['rule'])) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.order'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + // Contrôle si la nouvelle règle peut-être valide + if( strlen($myddlewareSession['param']['rule']['rulename']) < 3 || $myddlewareSession['param']['rule']['rulename_valide'] == false) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.valid'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + try { + // ---- Mode update ---- + if(!isset($myddlewareSession['param']['rule']['source']['module']) && !isset($myddlewareSession['param']['rule']['cible']['module'])) { + // RELOAD : Chargement des données d'une règle en édition + $myddlewareSession['param']['rule']['source']['module'] = $this->getRequest()->request->get('source_module'); + $myddlewareSession['param']['rule']['cible']['module'] = $this->getRequest()->request->get('cible_module'); + } + // ---- Mode update ---- + + // Connexion au service de la solution source + $solution_source_nom = $myddlewareSession['param']['rule']['source']['solution']; + $solution_source = $this->get('myddleware_rule.'.$solution_source_nom); + // $solution_source->login($this->decrypt_params($myddlewareSession['param']['rule']['source'])); + $solution_source->login($myddlewareSession['param']['rule']['source']); + + // Contrôle que la connexion est valide + if($solution_source->connexion_valide == false) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.source_module_connect'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + $modules = $solution_source->get_modules('source'); + + // SOURCE ------------------------------------------------------------------ + if($this->getRequest()->request->get('source_module')) { + $module['source'] = $this->getRequest()->request->get('source_module'); // mode create <<---- + } + else { + $module['source'] = $myddlewareSession['param']['rule']['source']['module']; // mode update <<---- + } + + // Met en mémoire la façon de traiter la date de référence + $myddlewareSession['param']['rule']['source']['datereference'] = $solution_source->referenceIsDate($module['source']); + + // Ajoute des champs source pour la validation + $rule_params_source = $solution_source->getFieldsParamUpd('source',$module['source']); + + // CIBLE ------------------------------------------------------------------ + // Si la solution est la même que la précèdente on récupère les infos + if($myddlewareSession['param']['rule']['source']['solution'] != $myddlewareSession['param']['rule']['cible']['solution']) { + $solution_cible_nom = $myddlewareSession['param']['rule']['cible']['solution']; + $solution_cible = $this->get('myddleware_rule.'.$solution_cible_nom); + } + else { + $solution_cible = $solution_source; + $solution_cible_nom = $solution_source_nom; + + if(empty($solution_cible->connexion_valide)) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.target_module_connect'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + } + } + + // Connexion vers la cible + // $solution_cible->login($this->decrypt_params($myddlewareSession['param']['rule']['cible'])); + $solution_cible->login($myddlewareSession['param']['rule']['cible']); + + if($solution_cible->connexion_valide == false) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.source_module_connect'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + if($this->getRequest()->request->get('cible_module')) { + $module['cible'] = $this->getRequest()->request->get('cible_module'); // mode create <<---- + } + else { + $module['cible'] = $myddlewareSession['param']['rule']['cible']['module']; // mode update <<---- + } + + // Récupère la liste des paramètres cible + $rule_params_target = $solution_cible->getFieldsParamUpd('target',$module['cible']); + + // Récupère la liste des champs cible + $rule_fields_target = $solution_cible->get_module_fields($module['cible'],'target'); + + // Ajoute des champs supplémentaires à la solution si elle le permet + $extend_fields = $solution_cible->extendField($module['cible']); + // SOURCE ----- Récupère la liste des champs source + if( $solution_source->get_submodules($module['source'],'source') != null ) { + if(isset($myddlewareSession['param']['rule']['source']['structure'])) { + $module['source'] = array($module['source'] => $myddlewareSession['param']['rule']['source']['structure']); + } + } + else { + if(isset($myddlewareSession['param']['rule']['source']['structure'])) { + unset($myddlewareSession['param']['rule']['source']['structure']); + } + } + + // Récupère la liste des champs source + $rule_fields_source = $solution_source->get_module_fields($module['source'],'source',$extend_fields); + + if($rule_fields_source) { + $myddlewareSession['param']['rule']['source']['fields'] = $rule_fields_source; + + // Erreur champs, pas de données sources (Exemple: GotoWebinar) + if(isset($myddlewareSession['param']['rule']['source']['fields']['error']) && !empty($myddlewareSession['param']['rule']['source']['fields']['error'])) { + $myddlewareSession['error']['create_rule'] = $myddlewareSession['param']['rule']['source']['fields']['error']; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + + // version 1.1.1 submodules + if(isset($myddlewareSession['param']['rule']['source']['structure'])) { + foreach ($rule_fields_source as $t => $k) { + foreach ($k as $submodules_name => $info_field) { + $source['table'][$t][$submodules_name] = $info_field['label']; + } + // Tri des champs sans tenir compte de la casse + asort($source['table'][$t], SORT_NATURAL | SORT_FLAG_CASE); + } + } + else { + foreach ($rule_fields_source as $t => $k) { + $source['table'][$module['source']][$t] = $k['label']; + } + // Tri des champs sans tenir compte de la casse + asort($source['table'][$module['source']], SORT_NATURAL | SORT_FLAG_CASE); + } + } + + // SOURCE ----- Récupère la liste des champs source + + // Type de synchronisation + // Récupération de tous les modes de règle possibles pour la cible et la source + $targetMode = $solution_cible->getRuleMode($module['cible'],'target'); + $sourceMode = $solution_source->getRuleMode($module['source'],'source'); + // Si la target à le type S (search) alors on l'ajoute à la source pour qu'il soit préservé par l'intersection + if (array_key_exists('S', $targetMode)) { + $sourceMode['S'] = 'search_only'; + } + $intersectMode = array_intersect($targetMode, $sourceMode); + // Si jamais l'intersection venait à être vide (ce qui ne devrait jamais arriver) on met par défaut le mode CREATE + if (empty($intersectMode)) { + $intersectMode['C'] = 'create_only'; + } + $myddlewareSession['param']['rule']['cible']['mode'] = $intersectMode; + + + // Préparation des champs cible + $cible['table'] = array(); + + if($rule_fields_target) { + + $myddlewareSession['param']['rule']['target']['fields'] = $rule_fields_target; + + $tmp = $rule_fields_target; + + $normal = array(); + $required = array(); + foreach ($rule_fields_target as $t => $k) { + + if(isset($k['required']) && $k['required'] == true) { + $required[] = $t; + } + else { + $normal[] = $t; + } + } + + asort($required); + asort($normal); + + $alpha = array_merge($required,$normal); + $field_target_alpha = array(); + foreach ($alpha as $name_fields) { + $field_target_alpha[$name_fields] = $tmp[$name_fields]['required']; + } + + $cible['table'][$module['cible']] = $field_target_alpha; + } + else { + $cible['table'][$module['cible']] = array(); // rev 1.1.1 + } + + $fieldMappingAdd = $solution_cible->getFieldMappingAdd($module['cible']); + + // On ajoute des champs personnalisés à notre mapping + if($fieldMappingAdd && isset($myddlewareSession['param']['rule']['last_version_id'])) { + + $ruleFields = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:RuleFields') + ->findByRule( $myddlewareSession['param']['rule']['last_version_id'] ); + + $tmp = array(); + foreach ($ruleFields as $fields) { + $tmp[$fields->getTarget()] = 0; + } + + foreach ($cible['table'][$module['cible']] as $k => $value) { + $tmp[ $k ] = $value; + } + + $cible['table'][$module['cible']] = $tmp; + + ksort($cible['table'][$module['cible']]); + } + +// ------------------- TARGET + + // Liste des relations TARGET + $relation = $solution_cible->get_module_fields_relate($module['cible']); + + $lst_relation_target = array(); + $lst_relation_target_alpha = array(); + if($relation) { + + foreach ($relation as $key => $value) { + $lst_relation_target[] = $key; + } + + asort($lst_relation_target); + + foreach ($lst_relation_target as $name_relate) { + $lst_relation_target_alpha[ $name_relate ]['required'] = $relation[ $name_relate ][ 'required_relationship' ]; + $lst_relation_target_alpha[ $name_relate ]['name'] = $name_relate; + $lst_relation_target_alpha[ $name_relate ]['label'] = (!empty($relation[ $name_relate ][ 'label' ]) ? $relation[ $name_relate ][ 'label' ] : $name_relate); + } + } + +// ------------------- SOURCE + // Liste des relations SOURCE + $relation_source = $solution_source->get_module_fields_relate($myddlewareSession['param']['rule']['source']['module']); + $lst_relation_source = array(); + $lst_relation_source_alpha = array(); + $choice_source = array(); + if($relation_source) { + + foreach ($relation_source as $key => $value) { + $lst_relation_source[] = $key; + } + + asort($lst_relation_source); + foreach ($lst_relation_source as $name_relate) { + $lst_relation_source_alpha[ $name_relate ]['label'] = $relation_source[ $name_relate ][ 'label' ]; + } + + // préparation de la liste en html + foreach ($lst_relation_source_alpha as $key => $value) { + $choice_source[ $key ] = (!empty($value['label']) ? $value['label'] : $key); + } + + } + + + if(!isset($source['table'])) { + $source['table'][ $myddlewareSession['param']['rule']['source']['module'] ] = array(); + } + + // -- Relation + // Liste des règles avec les mêmes connecteurs rev 1.07 + // + $stmt = $this->connection->prepare(' + SELECT r.rule_id, r.rule_name, r.rule_version + FROM Rule r + WHERE (conn_id_source=:id_source + AND conn_id_target=:id_target + AND r.rule_name != :name + AND r.rule_deleted = 0) + OR (conn_id_target=:id_source + AND conn_id_source=:id_target + AND r.rule_name != :name + AND r.rule_deleted = 0) + '); + $stmt->bindValue('id_source', (int)$myddlewareSession['param']['rule']['connector']['source'] ); + $stmt->bindValue('id_target', (int)$myddlewareSession['param']['rule']['connector']['cible'] ); + $stmt->bindValue('name', $myddlewareSession['param']['rule']['rulename'] ); + $stmt->execute(); + + $ruleListRelation = $stmt->fetchAll(); + + //Verson 1.1.1 : possibilité d'ajouter des relations custom en fonction du module source + $ruleListRelationSourceCustom = $solution_source->get_rule_custom_relationship($myddlewareSession['param']['rule']['source']['module'],'source'); + if (!empty($ruleListRelationSourceCustom)) { + $ruleListRelation = array_merge($ruleListRelation,$ruleListRelationSourceCustom); + } + + $choice = array(); + $control = array(); + + foreach ($ruleListRelation as $key => $value) { + + if(!in_array($value['rule_name'],$control) ) { + $choice[ $value['rule_id'] ] = $value['rule_name'].' - v'.$value['rule_version']; + $control[] = $value['rule_name']; + } + } + + asort($choice); + + // On récupére l'EntityManager + $this->getInstanceBdd(); + + // Récupère toutes les catégories + $lstCategory = $this->em->getRepository('RegleBundle:FuncCat') + ->findAll(); + + // Récupère toutes les functions + $lstFunctions = $this->em->getRepository('RegleBundle:Functions') + ->findAll(); + + // Champs pour éviter les doublons + $fieldsDuplicateTarget = $solution_cible->getFieldsDuplicate($myddlewareSession['param']['rule']['cible']['module']); + + // Les filtres + $lst_filter = array( + 'content' => $this->get('translator')->trans('filter.content'), + 'notcontent' => $this->get('translator')->trans('filter.notcontent'), + 'begin' => $this->get('translator')->trans('filter.begin'), + 'end' => $this->get('translator')->trans('filter.end'), + 'gt' => $this->get('translator')->trans('filter.gt'), + 'lt' => $this->get('translator')->trans('filter.lt'), + 'is' => $this->get('translator')->trans('filter.is'), + 'unlike'=> $this->get('translator')->trans('filter.unlike'), + 'gteq' => $this->get('translator')->trans('filter.gteq'), + 'lteq' => $this->get('translator')->trans('filter.lteq'), + 'in' => $this->get('translator')->trans('filter.in'), + 'notin' => $this->get('translator')->trans('filter.notin') + ); + + // paramètres de la règle + $rule_params = array_merge($rule_params_source,$rule_params_target); + + // récupération des champs de type liste -------------------------------------------------- + + // -----[ SOURCE ]----- + if(isset($myddlewareSession['param']['rule']['source']['fields'])) { + foreach($myddlewareSession['param']['rule']['source']['fields'] as $field => $fields_tab) { + if (array_key_exists('option', $fields_tab)) { + $formule_list['source'][$field] = $fields_tab; + } + } + } + + + if(isset($formule_list['source']) && count($formule_list['source']) > 0) { + foreach ($formule_list['source'] as $field => $fields_tab) { + foreach ($fields_tab['option'] as $field_name => $fields) { + if(!empty($fields)) { + $formule_list['source'][ $field ]['option'][ $field_name ] = $field_name . ' ( '.$fields.' )'; + } + } + } + } + + $html_list_source=''; + if( isset($formule_list['source']) ) { + foreach ($formule_list['source'] as $field => $fields_tab) { + $html_list_source.=''; + $html_list_source.= tools::composeListHtml($fields_tab['option']); + $html_list_source.=''; + } + } + + // -----[ TARGET ]----- + if(isset($myddlewareSession['param']['rule']['target']['fields'])) { + foreach($myddlewareSession['param']['rule']['target']['fields'] as $field => $fields_tab) { + if (array_key_exists('option', $fields_tab)) { + $formule_list['target'][$field] = $fields_tab; + } + } + } + + if(isset($formule_list['target']) && count($formule_list['target']) > 0) { + foreach ($formule_list['target'] as $field => $fields_tab) { + foreach ($fields_tab['option'] as $field_name => $fields) { + if(!empty($fields)) { + $formule_list['target'][ $field ]['option'][ $field_name ] = $field_name . ' ( '.$fields.' )'; + } + } + } + } + + $html_list_target=''; + if( isset($formule_list['target']) ) { + foreach ($formule_list['target'] as $field => $fields_tab) { + $html_list_target.=''; + $html_list_target.= tools::composeListHtml($fields_tab['option']); + $html_list_target.=''; + } + } + + // récupération des champs de type liste -------------------------------------------------- + + + // Type de synchronisation de données rev 1.06 -------------------------- + if(isset($myddlewareSession['param']['rule']['cible']['mode'])) { + + $mode_translate = array(); + foreach ($myddlewareSession['param']['rule']['cible']['mode'] as $key => $value) { + $mode_translate[ $key ] = $this->get('translator')->trans('create_rule.step3.syncdata.'.$value); + } + + $mode = + array( + array( + 'id' => 'mode', + 'name' => 'mode', + 'required' => false, + 'type' => 'option', + 'label' => $this->get('translator')->trans('create_rule.step3.syncdata.label'), + 'option' => $mode_translate + ) + ); + + $rule_params = array_merge($rule_params,$mode); + } + // Type de synchronisation de données rev 1.06 -------------------------- + + + // rev 1.07 -------------------------- + $bidirectional_params['connector']['source'] = $myddlewareSession['param']['rule']['connector']['source']; + $bidirectional_params['connector']['cible'] = $myddlewareSession['param']['rule']['connector']['cible']; + $bidirectional_params['module']['source'] = $module['source']; + $bidirectional_params['module']['cible'] = $module['cible']; + + $bidirectional = RuleClass::getBidirectionalRules( $this->get('database_connection'), $bidirectional_params ); + if($bidirectional) { + $rule_params = array_merge($rule_params,$bidirectional); + } + // rev 1.07 -------------------------- + $result = array( + 'source'=>$source['table'], + 'cible'=>$cible['table'], + 'rule_params'=>$rule_params, + 'lst_relation_target'=>$lst_relation_target_alpha, + 'lst_relation_source'=>$choice_source, + 'lst_rule' =>$choice, + 'lst_category' => $lstCategory, + 'lst_functions' => $lstFunctions, + 'lst_filter' =>$lst_filter, + 'params' => $myddlewareSession['param']['rule'], + 'duplicate_target' => $fieldsDuplicateTarget, + 'opt_target' => $html_list_target, + 'opt_source' => $html_list_source, + 'fieldMappingAddListType' => $fieldMappingAdd + ); + + // $result = $this->beforeRender($source['table'],$cible['table'],$rule_params,$lst_relation_target_alpha,$choice_source,$choice,$lstCategory,$lstFunctions,$lst_filter,$myddlewareSession['param']['rule'],$fieldsDuplicateTarget,$html_list_source,$fieldMappingAdd); + $result = $this->beforeRender($result); + + // Formatage des listes déroulantes : + $result['lst_relation_source'] = tools::composeListHtml($result['lst_relation_source'], $this->get('translator')->trans('create_rule.step3.relation.fields')); + $result['lst_rule'] = tools::composeListHtml($result['lst_rule'], $this->get('translator')->trans('create_rule.step3.relation.fields')); + $result['lst_filter'] = tools::composeListHtml($result['lst_filter'], $this->get('translator')->trans('create_rule.step3.relation.fields')); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Rule:create/step3.html.twig',$result); + + // ---------------- + } + catch(Exception $e) { + $myddlewareSession['error']['create_rule'] = $this->get('translator')->trans('error.rule.mapping'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('regle_stepone_animation')); + exit; + } + } + + protected function beforeRender($result) { + return $result; + } + + // Indique des informations concernant le champ envoyé en paramètre + public function infoFieldAction($field,$type) { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + if(isset($field) && !empty($field) && isset($myddlewareSession['param']['rule']) && $field != 'my_value') { + if(isset($myddlewareSession['param']['rule'][ $type ]['fields'][ $field ])) { + return $this->render('RegleBundle:Rule:create/onglets/info.html.twig',array( + 'field' => $myddlewareSession['param']['rule'][ $type ]['fields'][ $field ], + 'name' => htmlentities( trim($field) ) + ) + ); + } else { // Possibilité de Mutlimodules + foreach ($myddlewareSession['param']['rule'][ $type ]['fields'] as $subModule) { // Ce foreach fonctionnera toujours + if(isset($subModule[$field])) { // On teste si ça existe pour éviter une erreur PHP éventuelle + return $this->render('RegleBundle:Rule:create/onglets/info.html.twig',array( + 'field' => $subModule[$field], + 'name' => htmlentities( trim($field) ) + ) + ); + } + } + // On retourne vide si on l'a pas trouvé précédemment + return $this->render('RegleBundle:Rule:create/onglets/info.html.twig',array( + 'field' => '' + ) + ); + } + } + else { + return $this->render('RegleBundle:Rule:create/onglets/info.html.twig',array( + 'field' => '' + ) + ); + } + + } + + // CREATION - STEP THREE - VERIF DES FORMULES + public function ruleVerifFormulaAction(){ + + $request = $this->get('request'); + + if($request->getMethod()=='POST') { + + // Mise en place des variables + $formule = $this->get('formula.myddleware'); // service formule myddleware + $formule->init($this->getRequest()->request->get('formula')); // mise en place de la règle dans la classe + $formule->generateFormule(); // Genère la nouvelle formule à la forme PhP + + echo $formule->parse['error']; + exit; + } + else { + throw $this->createNotFoundException('Error'); + } + } + + // CREATION - STEP THREE - Validation du formulaire + public function ruleValidationAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // Array with the objects list flush in the database in case we have to rollback + $objectToRemove = array(); + $createTableRule = array(); + + // On récupére l'EntityManager + $this->getInstanceBdd(); + + try { + $request = $this->get('request'); + + // retourne un tableau prêt à l'emploi + $tab_new_rule = $this->createListeParamsRule( + $this->getRequest()->request->get('champs'), // Fields + $this->getRequest()->request->get('formules'), // Formula + $this->getRequest()->request->get('params') // Params + ); + + // fields relate + if(!empty( $this->getRequest()->request->get('duplicate') )) { + $tab_new_rule['params']['rule']['duplicate_fields'] = implode($this->getRequest()->request->get('duplicate'),';'); + } + + // si le nom de la règle est inferieur à 3 caractères : + if(strlen($myddlewareSession['param']['rule']['rulename']) < 3 || $myddlewareSession['param']['rule']['rulename_valide'] == false) { + echo 0; + exit; + } + + //------------ Create rule + + + if(isset($myddlewareSession['param']['rule']['version']) && !empty($myddlewareSession['param']['rule']['version'])) { + $version = $myddlewareSession['param']['rule']['version']; + } + else { + $version = '001'; + } + + $connector_source = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Connector') + ->findOneById( $myddlewareSession['param']['rule']['connector']['source'] ); + + $connector_target = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Connector') + ->findOneById( $myddlewareSession['param']['rule']['connector']['cible'] ); + + $param = RuleClass::getFieldsParamDefault(); + + $oneRule = new Rule(); + $oneRule->setConnectorSource( $connector_source ); + $oneRule->setConnectorTarget( $connector_target ); + $oneRule->setDateCreated(new \DateTime); + $oneRule->setDateModified(new \DateTime); + $oneRule->setCreatedBy( $this->getUser()->getId() ); + $oneRule->setModifiedBy( $this->getUser()->getId() ); + $oneRule->setModuleSource( $myddlewareSession['param']['rule']['source']['module'] ); + $oneRule->setModuleTarget( $myddlewareSession['param']['rule']['cible']['module'] ); + $oneRule->setDeleted( 0 ); + $oneRule->setActive( (int)$param['active'] ); + $oneRule->setName( $myddlewareSession['param']['rule']['rulename'] ); + $oneRule->setVersion( $version ); + + $this->em->persist($oneRule); + $objectToRemove[] = $oneRule; + // On fait le flush pour obtenir le nameSlug. En cas de problème on fait un remove dans le catch + $this->em->flush(); + + $myddlewareSession['rule']['newid'] = $oneRule->getId(); + + $nameRule = $oneRule->getNameSlug(); + + // BEFORE SAVE rev 1.08 ---------------------- + $relationshipsBeforeSave = $this->getRequest()->request->get('relations'); + $before_save = RuleClass::beforeSave($this->container, + array('ruleName' => $nameRule, + 'ruleVersion' => $version, + 'oldRule' => (empty($myddlewareSession['param']['rule']['last_version_id']) ? '' : $myddlewareSession['param']['rule']['last_version_id']), + 'connector' => $myddlewareSession['param']['rule']['connector'], + 'content' => $tab_new_rule, + 'relationships' => $relationshipsBeforeSave, + 'module' => array( + 'source' => + array( + 'solution' => $myddlewareSession['param']['rule']['source']['solution'], + 'name' => $myddlewareSession['param']['rule']['source']['module'] + ), + 'target' => + array( + 'solution' => $myddlewareSession['param']['rule']['cible']['solution'], + 'name' => $myddlewareSession['param']['rule']['cible']['module'] + ), + ) + ) + ); + if( !$before_save['done'] ) { + throw new \Exception($before_save['message']); + } + // Si le retour du beforeSave contient des paramètres alors on les ajoute à la règle avant sauvegarde + if (!empty($before_save['params'])) { + if (empty($tab_new_rule['params'])) { + $tab_new_rule['params'] = $before_save['params']; + } + else { + $tab_new_rule['params'] = array_merge($tab_new_rule['params'],$before_save['params']); + } + } + // Détection règle root ou child rev 1.08 ---------------------- + + + // mode update + if($oneRule->getId() && isset($myddlewareSession['param']['rule']['last_version_id'])) { + + foreach (json_decode($myddlewareSession['param']['rule']['reload']['params']) as $p) { + if($p->name == 'datereference') { + $date_reference = $p->value; // recup ancienne date de référence + } + } + // On met l'ancienne version à deleted ! + $lastRuleVersion = $this->em->getRepository('RegleBundle:Rule')->find($myddlewareSession['param']['rule']['last_version_id']); + $lastRuleVersion->setDeleted( 1 ); + $lastRuleVersion->setActive( 0 ); + $this->em->persist($lastRuleVersion); + $objectToRemove[] = $lastRuleVersion; + $this->em->flush(); + + // On adapte les relations + $updRuleRelationShips = $this->em->getRepository('RegleBundle:RuleRelationShips')->findByfieldId($myddlewareSession['param']['rule']['last_version_id']); + if(count($updRuleRelationShips) > 0 ) { + foreach ($updRuleRelationShips as $index => $field) { + $id = $updRuleRelationShips[$index]->getId(); + $updRuleRelationShipsTemp = $this->em->getRepository('RegleBundle:RuleRelationShips')->find( $id ); + $updRuleRelationShipsTemp->setFieldId( $myddlewareSession['rule']['newid'] ); + $this->em->persist($updRuleRelationShipsTemp); + $objectToRemove[] = $updRuleRelationShipsTemp; + $this->em->flush(); + } + } + // rev 1.07 Bidirectionnel + $stmt = $this->connection->prepare('UPDATE RuleParams SET rulep_value = :rulep_value_new + WHERE rulep_value = :rulep_value_old + AND rulep_name = "bidirectional"'); + $stmt->bindValue(":rulep_value_new", $oneRule->getId()); + $stmt->bindValue(":rulep_value_old", $myddlewareSession['param']['rule']['last_version_id']); + $stmt->execute(); + // rev 1.07 Bidirectionnel + } + else { + // mode create + if(isset($myddlewareSession['param']['rule']['source']['datereference']) && $myddlewareSession['param']['rule']['source']['datereference']) { + $date_reference = date('Y-m-d 00:00:00'); + } + else { + $date_reference = ''; + } + } + + //------------------------------- Create rule params ------------------- + if(isset($tab_new_rule['params']) || isset($param['RuleParams'])) { + + if(!isset($tab_new_rule['params'])) { + $p = $param['RuleParams']; + } + else { + $p = array_merge($param['RuleParams'],$tab_new_rule['params']); + } + + if(isset($myddlewareSession['param']['rule']['source']['structure'])) { + $p['structure'] = json_encode($myddlewareSession['param']['rule']['source']['structure']); + } + + foreach($p as $key => $value) { + + $oneRuleParams = new RuleParams(); + $oneRuleParams->setRule( $oneRule->getId() ); + + // si tableau de doublon + if($key == 'rule') { + $oneRuleParams->setName( 'duplicate_fields' ); + $oneRuleParams->setValue( $value['duplicate_fields'] ); + } + else { + + $oneRuleParams->setName( $key ); + if($key == 'datereference') { + // date de référence change en fonction create ou update + $oneRuleParams->setValue( $date_reference ); + } + else { + $oneRuleParams->setValue( $value ); + } + } + // Update the opposite rule if birectional rule + if($key == 'bidirectional') { + // Check if the parameter already exist + $stmt = $this->connection->prepare(' + SELECT * FROM RuleParams + WHERE + rulep_name = :rulep_name + AND rule_id = :rule_id + AND rulep_value = :rulep_value'); + $stmt->bindValue('rulep_name', 'bidirectional'); + $stmt->bindValue('rule_id', $value); + $stmt->bindValue('rulep_value', $oneRule->getId()); + $stmt->execute(); + $ruleParamBidirectional = $stmt->fetch(); + if (empty($ruleParamBidirectional)) { + $bidirectionaleRuleParams = new RuleParams(); + $bidirectionaleRuleParams->setRule( $value ); + $bidirectionaleRuleParams->setName( $key ); + $bidirectionaleRuleParams->setValue( $oneRule->getId() ); + $this->em->persist($bidirectionaleRuleParams); + $objectToRemove[] = $bidirectionaleRuleParams; + } + } + + $this->em->persist($oneRuleParams); + $objectToRemove[] = $oneRuleParams; + $this->em->flush(); + } + } + + //------------------------------- Create rule fields ------------------- + $fields_source = array(); + $fields_target = array(); + $debug = array(); + + if(isset($tab_new_rule['fields'])) { + + foreach ($tab_new_rule['fields']['name'] as $field_target => $c) { + + $fields_target[] = trim($field_target); + $field_source =""; + if(isset($c['champs'])) { + foreach ($c['champs'] as $name) { + $field_source .= $name.";"; + $fields_source[] = $name; + } + + $field_source = trim($field_source,";"); + } + + // Formula + $formule =""; + if(isset($c['formule'])) { + foreach ($c['formule'] as $name) { + $formule .= $name." "; + $debug[] = $name." "; + } + } + + // delete space + $field_source = str_replace(' ', '', $field_source); + + // Insert + $oneRuleFields = new RuleFields(); + $oneRuleFields->setRule( $oneRule->getId() ); + $oneRuleFields->setTarget( trim($field_target) ); + $oneRuleFields->setSource( ((!empty($field_source)) ? $field_source : 'my_value' ) ); + $oneRuleFields->setFormula( ((!empty($formule)) ? trim($formule) : NULL ) ); + $this->em->persist($oneRuleFields); + $objectToRemove[] = $oneRuleFields; + $this->em->flush(); + } + } + + //------------------------------- RELATIONSHIPS ------------------- + $tabRelationShips = array(); + if( !is_null($this->getRequest()->request->get('relations')) ) { + foreach ( $this->getRequest()->request->get('relations') as $rel ) { + if ( + !empty($rel['rule']) + && !empty($rel['source']) + ) { + // Creation dans la table RelationShips + $oneRuleRelationShips = new RuleRelationShips(); + $oneRuleRelationShips->setRule( $oneRule->getId() ); + $oneRuleRelationShips->setFieldNameSource( $rel['source'] ); + $oneRuleRelationShips->setFieldNameTarget( $rel['target'] ); + $oneRuleRelationShips->setFieldId( $rel['rule'] ); + + $tabRelationShips['target'][] = $rel['target']; + $tabRelationShips['source'][] = $rel['source']; + + $this->em->persist($oneRuleRelationShips); + $objectToRemove[] = $oneRuleRelationShips; + $this->em->flush(); + } + } + } + + //------------------------------- RuleFilters ------------------------ + + if( count( $this->getRequest()->request->get('filter') ) > 0 ) { + foreach ( $this->getRequest()->request->get('filter') as $filter ) { + $oneRuleFilters = new RuleFilters(); + $oneRuleFilters->setTarget( $filter['target'] ); + $oneRuleFilters->setRule( $oneRule->getId() ); + $oneRuleFilters->setType( $filter['filter'] ); + $oneRuleFilters->setValue( $filter['value'] ); + $this->em->persist($oneRuleFilters); + $objectToRemove[] = $oneRuleFilters; + $this->em->flush(); + } + } + + //------------------------------- ADD TABLE SOURCE ------------------- + + $fields_source = array_unique($fields_source); + $ps = array( + 'rule_name' => $nameRule, + 'rule_version' => $version, + 'fields_rule' => array_merge($fields_source,((isset($tabRelationShips['source'])) ? $tabRelationShips['source'] : array())), + 'fields_all' => ((isset($myddlewareSession['param']['rule']['source']['fields']) ? $myddlewareSession['param']['rule']['source']['fields'] : '')), + 'type' => 'source' + ); + $createTableRule[] = $this->createTableRule( $ps ); + + //------------------------------- ADD TABLE TARGET ------------------- + $fields_target_tab = ((isset($myddlewareSession['param']['rule']['target']['fields'])) ? $myddlewareSession['param']['rule']['target']['fields'] : array() ); + $fields_target_rule = array_merge($fields_target,((isset($tabRelationShips['target'])) ? $tabRelationShips['target'] : array())); + $fields_target = array_unique($fields_target); + $pt = array( + 'rule_name' => $nameRule, + 'rule_version' => $version, + 'fields_rule' => $fields_target_rule, + 'fields_all' => $fields_target_tab, + 'type' => 'target', + ); + + $createTableRule[] = $this->createTableRule( $pt ); + + //------------------------------- ADD TABLE TARGET HISTORY------------------- + + $ph = array( + 'rule_name' => $nameRule, + 'rule_version' => $version, + 'fields_rule' => $fields_target_rule, + 'fields_all' => $fields_target_tab, + 'type' => 'history', + ); + + $createTableRule[] = $this->createTableRule( $ph ); + + $job = $this->get('myddleware_job.job'); + $job->orderRules(); + + + + // -------------------------------------------------------------------------------------------------- + + // notification + $solution_source = $this->get('myddleware_rule.'.$myddlewareSession['param']['rule']['source']['solution']); + $solution_source->setMessageCreateRule($myddlewareSession['param']['rule']['source']['module']); + + $solution_target = $this->get('myddleware_rule.'.$myddlewareSession['param']['rule']['cible']['solution']); + $solution_target->setMessageCreateRule($myddlewareSession['param']['rule']['cible']['module']); + // notification + + // -------------------------------------------------------------------------------------------------- + + + // Détection règle root ou child rev 1.08 ---------------------- + // On réactualise les paramètres + $tab_new_rule['content']['params'] = $p; + RuleClass::afterSave($this->container, array( + 'ruleId' => $oneRule->getId(), + 'ruleName' => $nameRule, + 'ruleVersion' => $version, + 'oldRule' => (empty($myddlewareSession['param']['rule']['last_version_id']) ? '' : $myddlewareSession['param']['rule']['last_version_id']), + 'datereference' => $date_reference, + 'connector' => $myddlewareSession['param']['rule']['connector'], + 'content' => $tab_new_rule, + 'relationships' => $relationshipsBeforeSave, + 'module' => array( + 'source' => + array( + 'solution' => $myddlewareSession['param']['rule']['source']['solution'], + 'name' => $myddlewareSession['param']['rule']['source']['module'] + ), + 'target' => + array( + 'solution' => $myddlewareSession['param']['rule']['cible']['solution'], + 'name' => $myddlewareSession['param']['rule']['cible']['module'] + ), + ) + ) + ); + if(isset($myddlewareSession['param']['rule'])) { + unset( $myddlewareSession['param']['rule'] ); + } + $this->em->close(); + echo 1; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + }catch(\Exception $e) { + // Suppression des objets doctrine créés + if (!empty($objectToRemove)) { + foreach($objectToRemove as $object) { + $this->em->remove($object); + } + } + $this->em->flush(); + $this->em->close(); + // Suppression des tables créées + if (!empty($createTableRule)) { + foreach($createTableRule as $table) { + $this->connection->executeQuery( "DROP TABLE IF EXISTS z_".$table); + } + } + echo '2;'.htmlentities($e->getMessage().' (line '.$e->getLine().')'); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + exit; + } + } + + /* ****************************************************** + * TABLEAU DE BORD + ****************************************************** */ + public function panelAction() { + $language = $this->container->getParameter('locale'); + $myddleware_support = $this->container->getParameter('myddleware_support'); + + $this->getInstanceBdd(); + $solution = $this->em->getRepository('RegleBundle:Solution') + ->solutionActive(); + $lstArray = array(); + if($solution) { + foreach ($solution as $s) { + $lstArray[] = $s->getName(); + } + } + + $home = $this->get('myddleware.home'); + $permission = $this->get('myddleware.permission'); + $active = 0; + // Display buuton only if support is active + if ($myddleware_support) { + $objSupport = $this->getDoctrine()->getManager()->getRepository('LoginBundle:User')->findBy(array('username' => 'support')); + if (isset($objSupport[0])) { + $active = $objSupport[0]->isEnabled(); + } + } + $isAdmin = $permission->isAdmin($this->getUser()->getId()); + + $countTypeDoc = array(); + $nbFlux = 0; + + $listFlux = $home->countTypeDoc($isAdmin, $this->getUser()->getId()); + + foreach ($listFlux as $field => $value) { + $nbFlux = $nbFlux + (int)$value['nb']; + } + + return $this->render('RegleBundle:Home:index.html.twig',array( + 'errorByRule' => $home->errorByRule($isAdmin, $this->getUser()->getId()), + 'nbFlux' => $nbFlux, + 'solutions' => $lstArray, + 'locale' => $language + ) + ); + } + + public function graphTypeErrorAction() { + $home = $this->get('myddleware.home'); + $permission = $this->get('myddleware.permission'); + $countTypeDoc = array(); + $i=1; + foreach ($home->countTypeDoc($permission->isAdmin($this->getUser()->getId()), $this->getUser()->getId()) as $field => $value) { + if($i == 1) { + $countTypeDoc[] = array('test','test2'); + } + + $countTypeDoc[] = array($value['global_status'],(int)$value['nb']); + $i++; + } + + return new Response(json_encode($countTypeDoc)); + } + + public function graphTransferRuleAction() { + + $home = $this->get('myddleware.home'); + $permission = $this->get('myddleware.permission'); + $countTransferRule = array(); + $i=1; + foreach ($home->countTransferRule($permission->isAdmin($this->getUser()->getId()), $this->getUser()->getId()) as $field => $value) { + if($i == 1) { + $countTransferRule[] = array('test','test2'); + } + + $countTransferRule[] = array($value['rule_name'],(int)$value['nb']); + $i++; + } + + return new Response(json_encode($countTransferRule)); + } + + public function graphTransferHistoAction() { + $tools = new tools($this->get('logger'), $this->container, $this->get('database_connection')); + $home = $this->get('myddleware.home'); + $permission = $this->get('myddleware.permission'); + $countTransferRule = array(); + $i=1; + foreach ($home->countTransferHisto($permission->isAdmin($this->getUser()->getId()), $this->getUser()->getId()) as $field => $value) { + if($i == 1) { + $countTransferRule[] = array('date',$tools->getTranslation(array('flux', 'gbl_status', 'open')),$tools->getTranslation(array('flux', 'gbl_status', 'error')),$tools->getTranslation(array('flux', 'gbl_status', 'cancel')),$tools->getTranslation(array('flux', 'gbl_status', 'close'))); + } + + $countTransferRule[] = array($value['date'],(int)$value['open'],(int)$value['error'],(int)$value['cancel'],(int)$value['close']); + $i++; + } + return new Response(json_encode($countTransferRule)); + } + /* ****************************************************** + * ANIMATION + ****************************************************** */ + + public function listSubModulesAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // Traitement qui sauvegarde les sousmodules dans un array + if(isset($_POST['select']) && $_POST['select'] == 1 && isset($_POST['ids'])) { + $type = (($_POST['type'] == 'source') ? 'source' : 'target'); + try { + // si la structure n'est pas on va la créer + if(!isset($myddlewareSession['param']['rule'][$type]['structure'])) { + $myddlewareSession['param']['rule'][$type]['structure'] = array(); + } + + $ids = explode('__', $_POST['ids']); + + $structure = array(); + // composition des sousmodules sous forme de tableau en fonction des niveaux + if(count($ids) == 2) { // 1 niveau + $structure[ $ids[0] ] = $ids[1]; + + if(isset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]]) ) { + unset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]]); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo 2; + exit; // désélectionne un champ + } + } + else if(count($ids) == 3) { // 2 niveaux + $structure[ $ids[0] ] = array( $ids[2] => $ids[1] ); + + // si un champ est déjà sélectionné on le supprime des sessions + if(isset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]][$ids[2]])) { + unset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]][$ids[2]]); + + // Supprime son tableau parent si vide + if(count($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]]) == 0) { + unset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]]); + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo 2; + exit; // désélectionne un champ + } + } + else if(count($ids) == 4) { // 3 niveaux + $structure[ $ids[0] ] = array( $ids[1] => array( $ids[3] => $ids[2] ) ); + + if(isset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]][$ids[1]][$ids[3]]) ) { + unset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]][$ids[1]][$ids[3]]); + + // Supprime son tableau parent si vide + if(count($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]][$ids[1]]) == 0) { + unset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]][$ids[1]]); + } + + // Supprime son tableau parent si vide + if(count($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]]) == 0) { + unset($myddlewareSession['param']['rule'][$type]['structure'][$ids[0]]); + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo 2; + exit; // désélectionne un champ + } + } + + $fusion = array_merge_recursive($structure,$myddlewareSession['param']['rule'][$type]['structure']); + $myddlewareSession['param']['rule'][$type]['structure'] = $fusion; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo 1; + exit; + } + catch(\Exception $e) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + echo $e->getMessage(); + exit; + } + } + + $type = (($_POST['type'] == 'source') ? 'source' : 'cible'); + // Traitement qui affiche la liste des sousmodules + $id_connector = (int)$_POST['connector']; + + $solution = $this->get('myddleware_rule.'.$myddlewareSession['param']['rule'][$type]['solution']); + + // $params_connexion = $this->decrypt_params($myddlewareSession['param']['rule'][$type]); + $params_connexion = $myddlewareSession['param']['rule'][$type]; + $params_connexion['idConnector'] = $id_connector; + + $solution->login( $params_connexion ); + $list_submodules = $solution->get_submodules($_POST['module'], $type); + + if(is_null($list_submodules)) { + return new Response(0); + } + else { + $rows = ''; + foreach ($list_submodules as $submodules ) { + foreach ($submodules as $name_submodule => $value_submodule) { + + $rows .=' + + + '; + } + } + + $rows .= '
'.$name_submodule.''; + + if( !is_array( $value_submodule ) ) { + $id_span = str_replace(' ', '', $name_submodule.'__'.$value_submodule); + $rows .='

'.$name_submodule.' '.$value_submodule.'

'; + } + else { + foreach ($value_submodule as $id => $name) { + if( !is_array( $name ) ) { + $id_span = str_replace(' ', '', $name_submodule.'__'.$name.'__'.$id); + $rows .='

'.$name_submodule.' '.$name.'

'; + } + else { + foreach ($name as $id2 => $name2) { + if( !is_array( $name2 ) ) { + $id_span = str_replace(' ', '', $name_submodule.'__'.$id.'__'.$name2.'__'.$id2); + $rows .='

'.$name_submodule.' '.$id.' '.$name2.'

'; + } + } + } + } + } + '
'; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($rows); + } + } + + // VALIDATION DE L ANIMATION + public function validationAnimationAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + try{ + if(isset($_POST['choice_select'])) { + + if($_POST['choice_select'] == 'module') { + + // si le nom de la règle est inferieur à 3 caractères : + if(!isset($myddlewareSession['param']['rule']['source']['solution']) || strlen($myddlewareSession['param']['rule']['rulename']) < 3) { + $myddlewareSession['param']['rule']['rulename_valide'] = false; + } + else { + $myddlewareSession['param']['rule']['rulename_valide'] = true; + } + + $myddlewareSession['param']['rule']['source']['module'] = $_POST['module_source']; + $myddlewareSession['param']['rule']['cible']['module'] = $_POST['module_target']; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response('module'); + } + else if($_POST['choice_select'] == 'template') { + $myddlewareSession['param']['rule']['template']['id'] = $_POST['template']; + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + //-- + $template = $this->get('myddleware.template'); + $template->setIdConnectorSource( (int)$myddlewareSession['param']['rule']['connector']['source'] ); + $template->setIdConnectorTarget( (int)$myddlewareSession['param']['rule']['connector']['cible'] ); + $template->setLang( mb_strtoupper($this->getRequest()->getLocale()) ); + $template->setIdUser( $this->getUser()->getId() ); + $template->setPrefixRuleName( $myddlewareSession['param']['rule']['rulename'] ); + + $convertTemplate = $template->convertTemplate($myddlewareSession['param']['rule']['template']['id']); + if($convertTemplate === true) { + return new Response('template'); + } + else { + return new Response('error convertTemplate : '.$convertTemplate); + } + //-- + } + else { + return new Response(0); + } + } + else { + return new Response(0); + } + } + catch(Exception $e) { + return new Response($e->getMessage()); + } + } + + // LISTE DES TEMPLATES + public function listTemplateAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $template = $this->get('myddleware.template'); + $template->setIdConnectorSource( (int)$myddlewareSession['param']['rule']['connector']['source'] ); + $template->setIdConnectorTarget( (int)$myddlewareSession['param']['rule']['connector']['cible'] ); + $template->setLang( mb_strtoupper($this->getRequest()->getLocale()) ); + $template->setIdUser( $this->getUser()->getId() ); + + if( count($template->getTemplates()) > 0 ) { + $rows=''; + foreach ($template->getTemplates() as $t) { + + $rows .=' + + '.$t['tpll_name'].' + '.$t['tpll_description'].' + '; + } + + return new Response(' + + + + + + + + + '.$rows.' + +
#'.$this->get('translator')->trans('animate.choice.name').''.$this->get('translator')->trans('animate.choice.description').'
'); + } + else { + return new Response(''); + } + } + + // CREATION - STEP ONE - ANIMATION + public function ruleStepOneAnimationAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + // s'il existe des vielles données on les supprime + if(isset( $myddlewareSession['param']['rule'] )) { + unset( $myddlewareSession['param']['rule'] ); + } + + if(isset( $myddlewareSession['param']['myddleware']['connector'] )) { + unset( $myddlewareSession['param']['myddleware']['connector'] ); + } + + // Détecte s'il existe des erreurs + if(isset($myddlewareSession['error']['create_rule']) && !empty($myddlewareSession['error']['create_rule']) ) { + $error = $myddlewareSession['error']['create_rule']; + unset($myddlewareSession['error']['create_rule']); + } + else { + $error = false; + } + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + // Detecte si la session est le support --------- + + // Liste source : solution avec au moins 1 connecteur + $this->getInstanceBdd(); + + $solutionSource = $this->em->getRepository('RegleBundle:Solution') + ->solutionConnector( 'source',$permission->isAdmin($this->getUser()->getId()), $this->getUser()->getId() ); + + if( count($solutionSource) > 0 ) { + foreach($solutionSource as $s) { + $source[] = $s->getName(); + } + + $myddlewareSession['param']['myddleware']['connector']['solution']['source'] = json_encode($source); + } + + // Liste target : solution avec au moins 1 connecteur + $solutionTarget = $this->em->getRepository('RegleBundle:Solution') + ->solutionConnector( 'target',$permission->isAdmin($this->getUser()->getId()), $this->getUser()->getId() ); + + if( count($solutionTarget) > 0 ) { + foreach($solutionTarget as $t) { + $target[] = $t->getName(); + } + + $myddlewareSession['param']['myddleware']['connector']['solution']['target'] = json_encode($target); + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Rule:create/step1simply.html.twig',array( + 'source' => $solutionSource, + 'target' => $solutionTarget, + 'error' => $error + ) + ); + } + + // LISTE DES MODULES POUR ANIMATION + public function ruleListModuleAction() { + + try { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + + $id_connector = (int)$_POST['id']; + $type = $_POST['type']; + + $this->getInstanceBdd(); + $connector = $this->em->getRepository('RegleBundle:Connector') + ->findById( $id_connector ); // infos connector + + $connectorParams = $this->em->getRepository('RegleBundle:ConnectorParams') + ->findByConnector( $id_connector ); // infos params connector + + foreach ($connectorParams as $p) { + $myddlewareSession['param']['rule'][$type][$p->getName()] = $p->getValue(); // params connector + } + + $myddlewareSession['param']['rule']['connector'][$type] = $id_connector; // id connector + $myddlewareSession['param']['rule'][$type]['solution'] = $connector[0]->getSolution()->getName(); // nom de la solution + + $solution = $this->get('myddleware_rule.'.$myddlewareSession['param']['rule'][$type]['solution']); + + // $params_connexion = $this->decrypt_params($myddlewareSession['param']['rule'][$type]); + $params_connexion = $myddlewareSession['param']['rule'][$type]; + $params_connexion['idConnector'] = $id_connector; + + $solution->login( $params_connexion ); + + $t = (($type == 'source') ? 'source' : 'target' ); + + $liste_modules = tools::composeListHtml($solution->get_modules($t), $this->get('translator')->trans('create_rule.step1.choose_module')); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response($liste_modules); + } + catch(\Exception $e) { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return new Response(''); + } + } + + /* ****************************************************** + * METHODES PRATIQUES + ****************************************************** */ + + // CREATION REGLE - STEP ONE : Liste des connecteurs pour un user + private function liste_connectorAction($type) { + + $this->getInstanceBdd(); + $solution = $this->em->getRepository('RegleBundle:Connector') + ->findAllConnectorByUser( $this->getUser()->getId(), $type ); // type = source ou target + $lstArray = array(); + if($solution) { + foreach ($solution as $s) { + $lstArray[$s['name'].'_'.$s['id_connector']] = ucfirst($s['label']); + } + } + + $lst_solution = tools::composeListHtml($lstArray,$this->get('translator')->trans('create_rule.step1.list_empty')); + + return $lst_solution; + } + + // CREATION REGLE - STEP THREE - Retourne les paramètres dans un bon format de tableau + private function createListeParamsRule($fields,$formula,$params) { + + $phrase_placeholder = $this->get('translator')->trans('rule.step3.placeholder'); + $tab = array(); + + // FIELDS ------------------------------------------ + if($fields) { + $champs = explode(';',$fields); + foreach($champs as $champ) { + $chp = explode('[=]',$champ); + + if($chp[0]) { + if($phrase_placeholder != $chp[1] && 'my_value' != $chp[1]) { + $tab['fields']['name'][$chp[0]]['champs'][] = $chp[1]; + } + } + } + } + + // FORMULA ----------------------------------------- + if($formula) { + $formules = explode(';',$formula); + + foreach($formules as $formule) { + $chp = explode('[=]',$formule); + if($chp[0]) { + if(!empty($chp[1])) { + $tab['fields']['name'][$chp[0]]['formule'][] = $chp[1]; + } + } + } + } + + // PARAMS ----------------------------------------- + if($params) { + foreach ($params as $k => $p) { + $tab['params'][$p['name']] = $p['value']; + } + } + + return $tab; + } + + // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete + private function nav_pagination($params, $orm = true) { + + /* + * adapter_em_repository = requete + * maxPerPage = integer + * page = page en cours + */ + + if(is_array($params)) { + /* DOC : + * $pager->setCurrentPage($page); + $pager->getNbResults(); + $pager->getMaxPerPage(); + $pager->getNbPages(); + $pager->haveToPaginate(); + $pager->hasPreviousPage(); + $pager->getPreviousPage(); + $pager->hasNextPage(); + $pager->getNextPage(); + $pager->getCurrentPageResults(); + */ + + $compact = array(); + + #On passe l’adapter au bundle qui va s’occuper de la pagination + if($orm) { + $compact['pager'] = new Pagerfanta( new DoctrineORMAdapter($params['adapter_em_repository']) ); + } + else { + $compact['pager'] = new Pagerfanta( new ArrayAdapter($params['adapter_em_repository']) ); + } + + + + #On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) + $compact['pager']->setMaxPerPage($params['maxPerPage']); + try { + $compact['entities'] = $compact['pager'] + #On indique au pager quelle page on veut + ->setCurrentPage($params['page']) + #On récupère les résultats correspondant + ->getCurrentPageResults(); + + $compact['nb'] = $compact['pager']->getNbResults(); + + } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { + #Si jamais la page n’existe pas on léve une 404 + throw $this->createNotFoundException("Cette page n'existe pas."); + } + + return $compact; + } + else { + return false; + } + } + + // Crée une table dans la base de données + private function createTableRule($p) { + $this->getInstanceBdd(); + // $encryption = $this->container->getParameter('encryption'); + + $name_table = $p['rule_name'].'_'.$p['rule_version'].'_'.$p['type']; + + // Creation de la table document + $sql_create = " + #SET GLOBAL innodb_file_per_table=1; + #SET GLOBAL innodb_file_format=Barracuda; + CREATE TABLE IF NOT EXISTS z_$name_table ( + id_$name_table varchar(255),"; + + // Boucle sur tous les champs de la source pour creer la table + if($p['fields_rule']) { + // Dédoublonage des tableau dans le cas où le champ est plusieurs fois (ticket 522) + $p['fields_rule'] = array_unique($p['fields_rule']); + foreach ($p['fields_rule'] as $field) { + + if(isset($p['fields_all'][trim($field)]['type_bdd'])) { + $sql_create.= $field." ".$p['fields_all'][trim($field)]['type_bdd'].","; + } + else { + $sql_create.= $field." varchar(255),"; + } + } + } + + $sql_create.= "PRIMARY KEY (id_$name_table)) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;"; + + $this->connection->executeQuery( $sql_create ); + + return $name_table; + } + +} + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Controller/DefaultController.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class DefaultController extends DefaultControllerCore { + + } +} + diff --git a/src/Myddleware/RegleBundle/Controller/FluxController.php b/src/Myddleware/RegleBundle/Controller/FluxController.php new file mode 100644 index 000000000..22f11cc41 --- /dev/null +++ b/src/Myddleware/RegleBundle/Controller/FluxController.php @@ -0,0 +1,742 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Response; + +use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Pagerfanta; +use Pagerfanta\Exception\NotValidCurrentPageException; + +use Myddleware\RegleBundle\Entity\Solution; +use Myddleware\RegleBundle\Entity\Connector; +use Myddleware\RegleBundle\Entity\ConnectorParams; +use Myddleware\RegleBundle\Entity\DocumentsAudit; + +use Myddleware\RegleBundle\Classes\document as doc; + +class FluxControllerCore extends Controller +{ + protected $llimitListFlux = 1000; + + /* ****************************************************** + * FLUX + ****************************************************** */ + + public function fluxErrorByRuleAction($id) { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $em = $this->getDoctrine()->getManager(); + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $list_fields_sql = + array('id' => $id + ); + } + else { + $list_fields_sql = + array( + 'id' => $id, + 'createdBy' => $this->getUser()->getId() + ); + } + // Detecte si la session est le support --------- + + // Infos des flux + $rule = $em->getRepository('RegleBundle:Rule') + ->findBy( $list_fields_sql ); + if($rule) { + $myddlewareSession['flux_filter']['c']['rule'] = $rule[0]->getId(); + $myddlewareSession['flux_filter']['c']['gblstatus'] = 'Error'; + $myddlewareSession['flux_filter']['where'] = "WHERE rule_id='".$rule[0]->getId()."' AND global_status='Error' "; + } + else { + unset($myddlewareSession['flux_filter']); + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect($this->generateUrl('flux_list')); + } + + // LISTE DES FLUX + public function fluxListAction($page) { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + //--- Liste status traduction + $lstStatusTwig = doc::lstStatus(); + foreach ($lstStatusTwig as $key => $value) { + $lstStatus[ $key ] = $this->get('translator')->trans( $value ); + } + asort($lstStatus); + //--- + + //--- Liste Global status traduction + $lstGblStatusTwig = doc::lstGblStatus(); + + foreach ($lstGblStatusTwig as $key => $value) { + $lstGblStatus[ $key ] = $this->get('translator')->trans( $value ); + } + asort($lstGblStatus); + //--- + + $em = $this->getDoctrine()->getManager(); + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin( $this->getUser()->getId()) ) { + + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findAll(); + } + else { + $list_fields_sql = + array('createdBy' => $this->getUser()->getId() + ); + + $rule = $em->getRepository('RegleBundle:Rule')->findBy($list_fields_sql); + } + // Detecte si la session est le support --------- + + // Liste des règles + $lstRuleName = array(); + if($rule) { + + foreach ($rule as $r) { + $lstRuleName[$r->getId()] = $r->getName().' - v'.$r->getVersion(); + } + + asort($lstRuleName); + } + + $form = $this->createFormBuilder() + + ->add('date_create_start','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['date_create_start'])) ? $myddlewareSession['flux_filter']['c']['date_create_start'] : false), + 'required'=> false, + 'attr' => array('class' => 'calendar'))) + + ->add('date_create_end','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['date_create_end'])) ? $myddlewareSession['flux_filter']['c']['date_create_end'] : false), + 'required'=> false, + 'attr' => array('class' => 'calendar'))) + + ->add('date_modif_start','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['date_modif_start'])) ? $myddlewareSession['flux_filter']['c']['date_modif_start'] : false), + 'required'=> false, + 'attr' => array('class' => 'calendar'))) + + ->add('date_modif_end','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['date_modif_end'])) ? $myddlewareSession['flux_filter']['c']['date_modif_end'] : false), + 'required'=> false, + 'attr' => array('class' => 'calendar'))) + + ->add('rule','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['rule'])) ? $myddlewareSession['flux_filter']['c']['rule'] : false), + 'required'=> false )) + + ->add('rule', 'choice', array( + 'choices' => $lstRuleName, + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['rule'])) ? $myddlewareSession['flux_filter']['c']['rule'] : false), + 'required' => false + )) + + ->add('status', 'choice', array( + 'choices' => $lstStatus, + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['status'])) ? $myddlewareSession['flux_filter']['c']['status'] : false), + 'required' => false + )) + + ->add('gblstatus', 'choice', array( + 'choices' => $lstGblStatus, + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['gblstatus'])) ? $myddlewareSession['flux_filter']['c']['gblstatus'] : false), + 'required' => false + )) + + ->add('click_filter', 'submit', array( + 'attr' => array( + 'class' => 'btn-mydinv' + ), + 'label' => $this->get('translator')->trans( 'list_flux.btn.filter' ), + )) + + ->add('source_id','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['source_id'])) ? $myddlewareSession['flux_filter']['c']['source_id'] : false), + 'required'=> false )) + + ->add('target_id','text', array( + 'data'=> ((isset($myddlewareSession['flux_filter']['c']['target_id'])) ? $myddlewareSession['flux_filter']['c']['target_id'] : false), + 'required'=> false )) + + ->getForm(); + // + $form->handleRequest( $this->get('request') ); + // condition d'affichage + $where = ((isset($myddlewareSession['flux_filter']['where']) ? $myddlewareSession['flux_filter']['where'] : '')); + $conditions = 0; + //---[ FORM ]------------------------- + if( $form->get('click_filter')->isClicked() ) { + $data = $this->getRequest()->get($form->getName()); + $where = 'WHERE '; + + if(!empty( $data['date_create_start'] ) && is_string($data['date_create_start'])) { + $where .= "date_created >= '".$data['date_create_start']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['date_create_start'] = $data['date_create_start']; + } + else { + unset($myddlewareSession['flux_filter']['c']['date_create_start']); + } + + if(!empty( $data['date_create_end'] ) && is_string($data['date_create_end'])) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "date_created <= '".$data['date_create_end']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['date_create_end'] = $data['date_create_end']; + } + else { + unset($myddlewareSession['flux_filter']['c']['date_create_end']); + } + + if(!empty( $data['date_modif_start'] ) && is_string($data['date_modif_start'])) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "date_modified >= '".$data['date_modif_start']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['date_modif_start'] = $data['date_modif_start']; + } + else { + unset($myddlewareSession['flux_filter']['c']['date_modif_start']); + } + + if(!empty( $data['date_modif_end'] ) && is_string($data['date_modif_end'])) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "date_modified <= '".$data['date_modif_end']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['date_modif_end'] = $data['date_modif_end']; + } + else { + unset($myddlewareSession['flux_filter']['c']['date_modif_end']); + } + + if(!empty( $data['rule'] ) && is_string($data['rule'])) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "rule_id='".trim($data['rule'])."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['rule'] = $data['rule']; + } + else { + unset($myddlewareSession['flux_filter']['c']['rule']); + } + + if(!empty( $data['status'] )) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "status='".$data['status']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['status'] = $data['status']; + } + else { + unset($myddlewareSession['flux_filter']['c']['status']); + } + + if(!empty( $data['gblstatus'] )) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "global_status='".$data['gblstatus']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['gblstatus'] = $data['gblstatus']; + } + else { + unset($myddlewareSession['flux_filter']['c']['gblstatus']); + } + + if(!empty( $data['target_id'] )) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "target_id LIKE '".$data['target_id']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['target_id'] = $data['target_id']; + } + else { + unset($myddlewareSession['flux_filter']['c']['target_id']); + } + + if(!empty( $data['source_id'] )) { + $where .= (($conditions > 0) ? "AND " : "" ); + $where .= "source_id LIKE '".$data['source_id']."' "; + $conditions++; + $myddlewareSession['flux_filter']['c']['source_id'] = $data['source_id']; + } + else { + unset($myddlewareSession['flux_filter']['c']['source_id']); + } + + // si aucun condition alors on vide le where + if( $conditions == 0 ) { + $where = ''; + } + } // end clicked + //---[ FORM ]------------------------- + + // si première page on stock les conditions + if($page == 1) { + if(!empty($where) || isset($myddlewareSession['flux_filter']['where'])) { + $myddlewareSession['flux_filter']['where'] = $where; + } + } + + // si pagination on récupère les conditions + if((int)$page > 1 && isset($myddlewareSession['flux_filter']['where'])) { + $where = $myddlewareSession['flux_filter']['where']; + } + + $cond = ((!empty($where)) ? 'AND' : 'WHERE' ); + + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + + if( $permission->isAdmin($this->getUser()->getId()) ) { + $user = ''; + } + else { + $user = $cond.' created_by = '.$this->getUser()->getId(); + } + // Detecte si la session est le support --------- + + + $conn = $this->get( 'database_connection' ); + + // Le nombre de flux affichés est limité + $sql = "SELECT d.*, u.username, r.rule_version, concat(r.rule_name, 'v', r.rule_version) rule_name + FROM Documents d + JOIN users u ON(u.id=d.created_by) + JOIN Rule r USING(rule_id) + $where + $user + ORDER BY date_modified DESC + LIMIT $this->llimitListFlux"; + $stmt = $conn->prepare($sql); + $stmt->execute(); + $r = $stmt->fetchall(); + + $compact = $this->nav_pagination(array( + 'adapter_em_repository' => $r, + 'maxPerPage' => $this->container->getParameter('pager'), + 'page' => $page + ),false); + + // Si tout se passe bien dans la pagination + if( $compact ) { + + // Si aucune règle + if( $compact['nb'] < 1 && !intval($compact['nb'])) { + $compact['entities'] = ''; + $compact['pager'] = ''; + } + + // Si on atteind la limit alors on récupère le nombre total de flux + if ($compact['nb'] >= $this->llimitListFlux) { + $sql = "SELECT count(*) nb + FROM Documents d + JOIN users u ON(u.id=d.created_by) + JOIN Rule r USING(rule_id) + $where + $user"; + $stmt = $conn->prepare($sql); + $stmt->execute(); + $count = $stmt->fetch(); + $compact['nb'] = $count['nb']; + } + + // affiche le bouton pour supprimer les filtres si les conditions proviennent du tableau de bord + if(isset($myddlewareSession['flux_filter']['c'])) { + $conditions = 1; + } + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->render('RegleBundle:Flux:list.html.twig',array( + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'], + 'form' => $form->createView(), + 'condition' => $conditions + ) + ); + } + else { + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + throw $this->createNotFoundException('Error'); + } + } + + // Supprime le filtre des flux + public function fluxListDeleteFilterAction() { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + if(isset($myddlewareSession['flux_filter'])) { + unset($myddlewareSession['flux_filter']); + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + } + + return $this->redirect($this->generateUrl('flux_list')); + } + + // Info d'un flux + public function fluxInfoAction($id,$page) { + + try { + $em = $this->getDoctrine()->getManager(); + + // Detecte si la session est le support --------- + $permission = $this->get('myddleware.permission'); + $list_fields_sql = array('id' => $id); + + // Infos des flux + $doc = $em->getRepository('RegleBundle:Documents') + ->findById($list_fields_sql); + if( !$permission->isAdmin($this->getUser()->getId()) ) { + if( + empty($doc[0]) + || $doc[0]->getCreatedBy() != $this->getUser()->getId() + ) { + return $this->redirect($this->generateUrl('flux_list')); + } + } + // Detecte si la session est le support --------- + + $rule = $em->getRepository('RegleBundle:Rule') + ->findOneById($doc[0]->getRule()); + + // Chargement des tables source, target, history + $source = $this->listeFluxTable($id,'source'); + $target = $this->listeFluxTable($id,'target'); + $history = $this->listeFluxTable($id,'history'); + $compact = $this->nav_pagination(array( + 'adapter_em_repository' => $em->getRepository('RegleBundle:Log') + ->findBy( + array('document'=> $id), + array('id' => 'DESC') + ), + 'maxPerPage' => $this->container->getParameter('pager'), + 'page' => $page + ),false);; + + $name_solution_target = $rule->getConnectorTarget()->getSolution()->getName(); + $solution_target = $this->get('myddleware_rule.'.$name_solution_target); + $solution_target = $solution_target->getDocumentButton( $doc[0]->getId() ); + $solution_target = (($solution_target == NULL) ? array() : $solution_target ); + + $name_solution_source = $rule->getConnectorSource()->getSolution()->getName(); + $solution_source = $this->get('myddleware_rule.'.$name_solution_source); + $solution_source = $solution_source->getDocumentButton( $doc[0]->getId() ); + $solution_source = (($solution_source == NULL) ? array() : $solution_source ); + + $list_btn = array_merge( $solution_target, $solution_source ); + + return $this->render('RegleBundle:Flux:view/view.html.twig',array( + 'source' => $source[0], + 'target' => $target[0], + 'history' => $history[0], + 'doc' => $doc[0], + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'], + 'rule' => $rule, + 'ctm_btn' => $list_btn + ) + ); + } + catch(Exception $e) { + return $this->redirect($this->generateUrl('flux_list')); + exit; + } + + } + + // Sauvegarde flux + public function fluxSaveAction() { + + $request = $this->get('request'); + + if($request->getMethod()=='POST') { + + $rule = $this->getDoctrine() + ->getManager() + ->getRepository('RegleBundle:Rule') + ->findOneById( $this->getRequest()->request->get('rule') ); + + $conn = $this->get( 'database_connection' ); + $name = $rule->getNameSlug().'_'.$rule->getVersion().'_target'; + $fields = strip_tags($this->getRequest()->request->get('fields')); + $value = strip_tags($this->getRequest()->request->get('value')); + + if(!empty($value)) { + $stmt = $conn->prepare("UPDATE $zName SET $fields = $value WHERE $nameId = :id"); + $stmt->bindValue('id', $this->getRequest()->request->get('flux') ); + $stmt->execute(); + + // On récupére l'EntityManager + $em = $this->getDoctrine() + ->getManager(); + // Insert in audit + $oneDocAudit = new DocumentsAudit(); + $oneDocAudit->setDoc( $this->getRequest()->request->get('flux') ); + $oneDocAudit->setDateModified( new \DateTime ); + $oneDocAudit->setAfter( $value ); + $oneDocAudit->setByUser( $this->getUser()->getId() ); + $oneDocAudit->setName( $fields ); + $em->persist($oneDocAudit); + $em->flush(); + } + echo $value; + exit; + + } + else { + throw $this->createNotFoundException('Error'); + } + } + + // Relancer un flux + public function fluxRerunAction($id) { + try { + $request = $this->get('request'); + $session = $request->getSession(); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $rule = $this->container->get('myddleware_rule.rule'); + $msg = $rule->actionDocument($id,'rerun'); + + $myddlewareSession['param']['myddleware']['note'][] = $msg; // alerte + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect( $this->generateURL('flux_info', array( 'id'=>$id )) ); + exit; + } + catch(Exception $e) { + return $this->redirect($this->generateUrl('flux_list')); + } + } + + // Annuler un flux + public function fluxCancelAction($id) { + try { + $request = $this->get('request'); + $session = $request->getSession(); + // $session = $this->container->get('session'); + $myddlewareSession = $session->getBag('flashes')->get('myddlewareSession'); + // We always add data again in session because these data are removed after the call of the get + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + $rule = $this->container->get('myddleware_rule.rule'); + $msg = $rule->actionDocument($id,'cancel'); + + $myddlewareSession['param']['myddleware']['note'][] = $msg; // alerte + $session->getBag('flashes')->set('myddlewareSession', $myddlewareSession); + return $this->redirect( $this->generateURL('flux_info', array( 'id'=>$id )) ); + exit; + } + catch(Exception $e) { + return $this->redirect($this->generateUrl('flux_list')); + } + } + + // Exécute une action d'un bouton dynamique + public function fluxBtnDynAction($method,$id,$solution) { + $solution_ws = $this->get('myddleware_rule.'.mb_strtolower($solution) ); + $solution_ws->documentAction( $id, $method ); + + return $this->redirect($this->generateUrl('flux_info', array('id'=>$id))); + } + + + public function fluxMassCancelAction() { + if(isset($_POST['ids']) && count($_POST['ids']) > 0) { + $job = $this->get('myddleware_job.job'); + $job->actionMassTransfer('cancel',$_POST['ids']); + } + + exit; + } + + public function fluxMassRunAction() { + + if(isset($_POST['ids']) && count($_POST['ids']) > 0) { + $job = $this->get('myddleware_job.job'); + $job->actionMassTransfer('rerun',$_POST['ids']); + } + + exit; + } + + /* ****************************************************** + * METHODES PRATIQUES + ****************************************************** */ + + // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete + private function nav_pagination($params, $orm = true) { + + /* + * adapter_em_repository = requete + * maxPerPage = integer + * page = page en cours + */ + + if(is_array($params)) { + /* DOC : + * $pager->setCurrentPage($page); + $pager->getNbResults(); + $pager->getMaxPerPage(); + $pager->getNbPages(); + $pager->haveToPaginate(); + $pager->hasPreviousPage(); + $pager->getPreviousPage(); + $pager->hasNextPage(); + $pager->getNextPage(); + $pager->getCurrentPageResults(); + */ + + $compact = array(); + + #On passe l’adapter au bundle qui va s’occuper de la pagination + if($orm) { + $compact['pager'] = new Pagerfanta( new DoctrineORMAdapter($params['adapter_em_repository']) ); + } + else { + $compact['pager'] = new Pagerfanta( new ArrayAdapter($params['adapter_em_repository']) ); + } + + + + #On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) + $compact['pager']->setMaxPerPage($params['maxPerPage']); + try { + $compact['entities'] = $compact['pager'] + #On indique au pager quelle page on veut + ->setCurrentPage($params['page']) + #On récupère les résultats correspondant + ->getCurrentPageResults(); + + $compact['nb'] = $compact['pager']->getNbResults(); + + } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { + #Si jamais la page n’existe pas on léve une 404 + throw $this->createNotFoundException("Cette page n'existe pas."); + } + + return $compact; + } + else { + return false; + } + } + + // Liste tous les flux d'un type + private function listeFluxTable($id,$type) { + + try { + $tools = $this->container->get('myddleware_tools.tools'); + $conn = $this->get( 'database_connection' ); + + // Documents + $stmt = $conn->prepare('SELECT rule_id + FROM Documents + WHERE id = :id'); + + $stmt->bindValue('id', $id); + $stmt->execute(); + $flux['source']['champ'] = $stmt->fetch(); + + // Regle + $stmt = $conn->prepare('SELECT rule_name_slug, rule_version + FROM Rule + WHERE rule_id = :id'); + $stmt->bindValue('id', $flux['source']['champ']['rule_id']); + $stmt->execute(); + $flux['source'] = $stmt->fetch(); + $flux['source']['table'] = $flux['source']['rule_name_slug'].'_'.$flux['source']['rule_version']; + + $table = 'z_'.$flux['source']['table'].'_'.$type; + $idName = 'id_'.$flux['source']['table'].'_'.$type; + $stmt = $conn->prepare("SELECT * + FROM $table + WHERE $idName = :id"); + $stmt->bindValue(':id', $id); + $stmt->execute(); + $flux['source']['data'] = $stmt->fetchAll(); + $first = true; + // Récupération des types de champs pour la table en cours + $fieldsDetail = $tools->describeTable($table); + + foreach ($flux['source']['data'] as $indice => $line) { + foreach ($line as $field => $value) { + if($first) { + $first = false; + $flux['source']['data'][$indice][$field] = $value; + continue; + } + else { + $flux['source']['data'][$indice][$field] = $value; + } + } + } + return array($flux['source']['data'],$id); + } + catch(Exception $e) { + return false; + } + } + +} + + +/* * * * * * * * * * * * * * * * * * * * + si custom file exist alors on fait un include de la custom class + * * * * * * * * * * * * * * * * * * * */ +$file = __DIR__.'/../Custom/Controller/FluxController.php'; +if(file_exists($file)){ + require_once($file); +} +else { + //Sinon on met la classe suivante + class FluxController extends FluxControllerCore { + + } +} + + diff --git a/src/Myddleware/RegleBundle/Controller/TaskController.php b/src/Myddleware/RegleBundle/Controller/TaskController.php new file mode 100644 index 000000000..1a6fd5bab --- /dev/null +++ b/src/Myddleware/RegleBundle/Controller/TaskController.php @@ -0,0 +1,197 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Response; +use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Adapter\ArrayAdapter; +use Pagerfanta\Pagerfanta; +use Pagerfanta\Exception\NotValidCurrentPageException; + +use Myddleware\RegleBundle\Entity\Log; + +class TaskController extends Controller +{ + + /* ****************************************************** + * TASK + ****************************************************** */ + + // Liste des tâches + public function taskListAction($page) { + + // List of task limited to 1000 and rder by status (start first) and date begin + $conn = $this->get( 'database_connection' ); + $sql = 'SELECT j.* + FROM Job j + ORDER BY job_status DESC, job_begin DESC + LIMIT 1000'; + + $r = $conn->executeQuery( $sql )->fetchAll(); + + $compact = $this->nav_pagination(array( + 'adapter_em_repository' => $r, + 'maxPerPage' => $this->container->getParameter('pager'), + 'page' => $page + ),false); + + return $this->render('RegleBundle:Task:list.html.twig',array( + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'] + ) + ); + } + + // Fiche d'une tâche + public function viewTaskAction($id,$page) { + + + try { + $em = $this->getDoctrine()->getManager(); + + // Infos de la tâche + $task = $em->getRepository('RegleBundle:Job') + ->findOneById($id); + + $compact = $this->nav_pagination(array( + 'adapter_em_repository' => $em->getRepository('RegleBundle:Log') + ->findBy( + array('job'=> $id), + array('id' => 'DESC') + ), + 'maxPerPage' => $this->container->getParameter('pager'), + 'page' => $page + ),false); + + return $this->render('RegleBundle:Task:view/view.html.twig',array( + 'task' => $task, + 'nb' => $compact['nb'], + 'entities' => $compact['entities'], + 'pager' => $compact['pager'] + ) + ); + + } catch(Exception $e) { + return $this->redirect($this->generateUrl('task_list')); + } + } + + // Stop task + public function stopTaskAction($id,$page) { + try { + $em = $this->getDoctrine()->getManager(); + + // Stop task + $taskStop = $em->getRepository('RegleBundle:Job')->findOneById($id); + $taskStop->setStatus( 'End' ); + $taskStop->setEnd( new \DateTime ); + $em->persist($taskStop); + + // Add log to indicate this action + $log = new Log(); + $log->setDateCreated(new \DateTime); + $log->setType('W'); + $log->setMessage('The task has been manually stopped. '); + $log->setRule(''); + $log->setDocument(''); + $log->setRef(''); + $log->setJob($id); + $em->persist($log); + $em->flush(); + + return $this->redirect( $this->generateURL('task_view', array( 'id'=>$id )) ); + } catch(Exception $e) { + return $this->redirect($this->generateUrl('task_list')); + } + } + + + /* ****************************************************** + * METHODES PRATIQUES + ****************************************************** */ + + // Crée la pagination avec le Bundle Pagerfanta en fonction d'une requete + private function nav_pagination($params, $orm = true) { + + /* + * adapter_em_repository = requete + * maxPerPage = integer + * page = page en cours + */ + + if(is_array($params)) { + /* DOC : + * $pager->setCurrentPage($page); + $pager->getNbResults(); + $pager->getMaxPerPage(); + $pager->getNbPages(); + $pager->haveToPaginate(); + $pager->hasPreviousPage(); + $pager->getPreviousPage(); + $pager->hasNextPage(); + $pager->getNextPage(); + $pager->getCurrentPageResults(); + */ + + $compact = array(); + + #On passe l’adapter au bundle qui va s’occuper de la pagination + if($orm) { + $compact['pager'] = new Pagerfanta( new DoctrineORMAdapter($params['adapter_em_repository']) ); + } + else { + $compact['pager'] = new Pagerfanta( new ArrayAdapter($params['adapter_em_repository']) ); + } + + + + #On définit le nombre d’article à afficher par page (que l’on a biensur définit dans le fichier param) + $compact['pager']->setMaxPerPage($params['maxPerPage']); + try { + $compact['entities'] = $compact['pager'] + #On indique au pager quelle page on veut + ->setCurrentPage($params['page']) + #On récupère les résultats correspondant + ->getCurrentPageResults(); + + $compact['nb'] = $compact['pager']->getNbResults(); + + } catch (\Pagerfanta\Exception\NotValidCurrentPageException $e) { + #Si jamais la page n’existe pas on léve une 404 + throw $this->createNotFoundException("Cette page n'existe pas."); + } + + return $compact; + } + else { + return false; + } + } + + +} diff --git a/src/Myddleware/RegleBundle/DataFixtures/ORM/LoadFunctionData.php b/src/Myddleware/RegleBundle/DataFixtures/ORM/LoadFunctionData.php new file mode 100644 index 000000000..cb38f8b43 --- /dev/null +++ b/src/Myddleware/RegleBundle/DataFixtures/ORM/LoadFunctionData.php @@ -0,0 +1,69 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\DataFixtures\ORM; + +use Doctrine\Common\DataFixtures\FixtureInterface; +use Doctrine\Common\Persistence\ObjectManager; +use Myddleware\RegleBundle\Entity\FuncCat; +use Myddleware\RegleBundle\Entity\Functions; + +class LoadFunctionData implements FixtureInterface +{ + private $manager; + protected $functionData = array( + 'mathematical' => array('round','ceil','abs'), + 'text' => array('trim','lower','upper','substr','striptags','changeValue','htmlEntityDecode','replace','utf8encode','utf8decode','htmlentities','htmlspecialchars'), + 'date' => array('date','microtime','changeTimeZone','changeFormatDate') + ); + + public function load(ObjectManager $manager){ + $this->manager = $manager; + $this->generateEntities(); + $this->manager->flush(); + } + + public function getOrder() { + return 1; + } + + private function generateEntities() { + foreach($this->functionData as $cat => $functions) { + $this->newEntity($cat,$functions); + } + } + + private function newEntity($cat,$functions) { + $funcCat = new FuncCat(); + $funcCat->setNameYml($cat); + $this->manager->persist($funcCat); + foreach($functions as $function) { + $func = new Functions(); + $func->setName($function); + $func->setCategorieId($funcCat); + $this->manager->persist($func); + } + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/DataFixtures/ORM/LoadSolutionData.php b/src/Myddleware/RegleBundle/DataFixtures/ORM/LoadSolutionData.php new file mode 100644 index 000000000..1f1361a7f --- /dev/null +++ b/src/Myddleware/RegleBundle/DataFixtures/ORM/LoadSolutionData.php @@ -0,0 +1,74 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\DataFixtures\ORM; + +use Doctrine\Common\DataFixtures\FixtureInterface; +use Doctrine\Common\Persistence\ObjectManager; +use Myddleware\RegleBundle\Entity\Solution; + +class LoadSolutionData implements FixtureInterface +{ + private $manager; + protected $solutionData = array( + array('name'=> 'sugarcrm' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'salesforce' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'prestashop' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'dolist' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'eventbrite' , 'active' => 1 , 'source' => 1 , 'target' => 0), + array('name'=> 'suitecrm' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'mailchimp' , 'active' => 1 , 'source' => 0 , 'target' => 1), + array('name'=> 'bittle' , 'active' => 1 , 'source' => 0 , 'target' => 1), + array('name'=> 'sagecrm' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'sapcrm' , 'active' => 0 , 'source' => 1 , 'target' => 1), + array('name'=> 'database' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'sapecc' , 'active' => 0 , 'source' => 1 , 'target' => 0), + array('name'=> 'magento' , 'active' => 1 , 'source' => 1 , 'target' => 1), + array('name'=> 'moodle' , 'active' => 1 , 'source' => 0 , 'target' => 1), + array('name'=> 'file' , 'active' => 1 , 'source' => 1 , 'target' => 0) + ); + + public function load(ObjectManager $manager){ + $this->manager = $manager; + $this->generateEntities(); + $this->manager->flush(); + } + + public function getOrder() { + return 1; + } + + private function generateEntities() { + foreach($this->solutionData as $solution) { + $sol = new Solution(); + $sol->setName($solution['name']); + $sol->setActive($solution['active']); + $sol->setSource($solution['source']); + $sol->setTarget($solution['target']); + $this->manager->persist($sol); + } + } + +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/DependencyInjection/Configuration.php b/src/Myddleware/RegleBundle/DependencyInjection/Configuration.php new file mode 100644 index 000000000..6ea9cfd83 --- /dev/null +++ b/src/Myddleware/RegleBundle/DependencyInjection/Configuration.php @@ -0,0 +1,52 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This is the class that validates and merges configuration from your app/config files + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} + */ +class Configuration implements ConfigurationInterface +{ + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $treeBuilder->root('regle'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/src/Myddleware/RegleBundle/DependencyInjection/RegleExtension.php b/src/Myddleware/RegleBundle/DependencyInjection/RegleExtension.php new file mode 100644 index 000000000..6ad01420a --- /dev/null +++ b/src/Myddleware/RegleBundle/DependencyInjection/RegleExtension.php @@ -0,0 +1,51 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader; + +/** + * This is the class that loads and manages your bundle configuration + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} + */ +class RegleExtension extends Extension +{ + /** + * {@inheritDoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $this->processConfiguration($configuration, $configs); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + } +} diff --git a/src/Myddleware/RegleBundle/Entity/Config.php b/src/Myddleware/RegleBundle/Entity/Config.php new file mode 100644 index 000000000..0694c2089 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Config.php @@ -0,0 +1,121 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Config + * @ORM\Table() + * @ORM\Entity + * @ORM\Table(indexes={@ORM\Index(name="Kconf_name", columns={"conf_name"})}) + */ +class Config +{ + /** + * @var integer + * + * @ORM\Column(name="conf_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var datetime + * + * + * @ORM\Column(name="conf_name", type="string", length=100, nullable=false) + * + */ + private $name; + + + /** + * @var string + * + * @ORM\Column(name="conf_value", type="string", length=255, nullable=false) + * + */ + private $value; + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set name + * + * @param string $name + * @return Config + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set value + * + * @param string $value + * @return Config + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get value + * + * @return string + */ + public function getValue() + { + return $this->value; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/ConfigRepository.php b/src/Myddleware/RegleBundle/Entity/ConfigRepository.php new file mode 100644 index 000000000..47a73ce57 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/ConfigRepository.php @@ -0,0 +1,48 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * ConfigRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class ConfigRepository extends EntityRepository +{ + // Retourne la version de Myddleware + public function myddlewareVersion() + { + return $this->createQueryBuilder('c') + ->select('c.value') + ->where('c.id = 1') + ->getQuery() + ->getOneOrNullResult(); + } + +} diff --git a/src/Myddleware/RegleBundle/Entity/Connector.php b/src/Myddleware/RegleBundle/Entity/Connector.php new file mode 100644 index 000000000..f53a5161a --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Connector.php @@ -0,0 +1,324 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Gedmo\Mapping\Annotation as Gedmo; // slug +use Doctrine\ORM\Mapping as ORM; + +/** + * Connector + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\ConnectorRepository") + */ +class Connector +{ + /** + * @var integer + * + * @ORM\Column(name="conn_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + + /** + * @var Solution $solution + * + * @ORM\ManyToOne(targetEntity="Solution", inversedBy="connector") + * @ORM\JoinColumn(name="sol_id", referencedColumnName="sol_id") + * + */ + private $solution; + + + /** + * @var ArrayCollection $rule + * + */ + private $rule; + + + /** + * @var string + * + * @ORM\Column(name="conn_lbl", type="string", length=50, nullable=false) + * + */ + private $label; + + /** + * @Gedmo\Slug(fields={"label"}, separator="_", unique=true) + * @ORM\Column(length=50, nullable=false, name="conn_lbl_slug") + */ + private $labelSlug; + + /** + * @var datetime + * + * + * @ORM\Column(name="conn_date_created", type="datetime", nullable=false) + * + */ + private $dateCreated; + + /** + * @var datetime + * + * @ORM\Column(name="conn_date_modified", type="datetime", nullable=false) + * + */ + private $dateModified; + + /** + * @var integer + * + * @ORM\Column(name="conn_created_by", nullable=false) + * + */ + private $createdBy; + + /** + * @var integer + * + * @ORM\Column(name="conn_modified_by", nullable=false) + * + */ + private $modifiedBy; + + /** + * Constructor + */ + public function __construct() + { + $this->rule = new \Doctrine\Common\Collections\ArrayCollection(); + } + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set label + * + * @param string $label + * @return Connector + */ + public function setLabel($label) + { + $this->label = $label; + + return $this; + } + + /** + * Get label + * + * @return string + */ + public function getLabel() + { + return $this->label; + } + + /** + * Set labelSlug + * + * @param string $labelSlug + * @return Connector + */ + public function setLabelSlug($labelSlug) + { + $this->labelSlug = $labelSlug; + + return $this; + } + + /** + * Get labelSlug + * + * @return string + */ + public function getLabelSlug() + { + return $this->labelSlug; + } + + /** + * Set dateCreated + * + * @param \DateTime $dateCreated + * @return Connector + */ + public function setDateCreated($dateCreated) + { + $this->dateCreated = $dateCreated; + + return $this; + } + + /** + * Get dateCreated + * + * @return \DateTime + */ + public function getDateCreated() + { + return $this->dateCreated; + } + + /** + * Set dateModified + * + * @param \DateTime $dateModified + * @return Connector + */ + public function setDateModified($dateModified) + { + $this->dateModified = $dateModified; + + return $this; + } + + /** + * Get dateModified + * + * @return \DateTime + */ + public function getDateModified() + { + return $this->dateModified; + } + + /** + * Set createdBy + * + * @param string $createdBy + * @return Connector + */ + public function setCreatedBy($createdBy) + { + $this->createdBy = $createdBy; + + return $this; + } + + /** + * Get createdBy + * + * @return string + */ + public function getCreatedBy() + { + return $this->createdBy; + } + + /** + * Set modifiedBy + * + * @param string $modifiedBy + * @return Connector + */ + public function setModifiedBy($modifiedBy) + { + $this->modifiedBy = $modifiedBy; + + return $this; + } + + /** + * Get modifiedBy + * + * @return string + */ + public function getModifiedBy() + { + return $this->modifiedBy; + } + + /** + * Set solution + * + * @param \Myddleware\RegleBundle\Entity\Solution $solution + * @return Connector + */ + public function setSolution(\Myddleware\RegleBundle\Entity\Solution $solution) + { + $this->solution = $solution; + + return $this; + } + + /** + * Get solution + * + * @return \Myddleware\RegleBundle\Entity\Solution + */ + public function getSolution() + { + return $this->solution; + } + + /** + * Add rule + * + * @param \Myddleware\RegleBundle\Entity\Rule $rule + * @return Connector + */ + public function addRule(\Myddleware\RegleBundle\Entity\Rule $rule) + { + $this->rule[] = $rule; + + return $this; + } + + /** + * Remove rule + * + * @param \Myddleware\RegleBundle\Entity\Rule $rule + */ + public function removeRule(\Myddleware\RegleBundle\Entity\Rule $rule) + { + $this->rule->removeElement($rule); + } + + /** + * Get rule + * + * @return \Doctrine\Common\Collections\Collection + */ + public function getRule() + { + return $this->rule; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/ConnectorParams.php b/src/Myddleware/RegleBundle/Entity/ConnectorParams.php new file mode 100644 index 000000000..83d36dc3d --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/ConnectorParams.php @@ -0,0 +1,151 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Connector_params + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\ConnectorParamsRepository") + */ +class ConnectorParams +{ + /** + * @var integer + * + * @ORM\Column(name="conp_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var integer + * + * @ORM\OneToOne(targetEntity="Myddleware\RegleBundle\Entity\Connector") + * @ORM\Column(name="conn_id", type="integer", nullable=false) + * @ORM\JoinColumn(name="conn_id", referencedColumnName="conn_id") + */ + private $connector; + + + /** + * @var string + * + * @ORM\Column(name="conp_name", type="string", length=684) + */ + private $name; + + /** + * @var string + * + * @ORM\Column(name="conp_value", type="string", length=684) + */ + private $value; + + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set connector + * + * @param integer $connector + * @return ConnectorParams + */ + public function setConnector($connector) + { + $this->connector = $connector; + + return $this; + } + + /** + * Get connector + * + * @return integer + */ + public function getConnector() + { + return $this->connector; + } + + /** + * Set name + * + * @param string $name + * @return ConnectorParams + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set value + * + * @param string $value + * @return ConnectorParams + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get value + * + * @return string + */ + public function getValue() + { + return $this->value; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/ConnectorParamsRepository.php b/src/Myddleware/RegleBundle/Entity/ConnectorParamsRepository.php new file mode 100644 index 000000000..75994a3e6 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/ConnectorParamsRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * ConnectorParamsRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class ConnectorParamsRepository extends EntityRepository +{ +} diff --git a/src/Myddleware/RegleBundle/Entity/ConnectorRepository.php b/src/Myddleware/RegleBundle/Entity/ConnectorRepository.php new file mode 100644 index 000000000..4781d7f10 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/ConnectorRepository.php @@ -0,0 +1,71 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * SolutionRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class ConnectorRepository extends EntityRepository +{ + // Liste des connecteurs pour un user et si les solutions sont actives + public function findAllConnectorByUser($id,$type) + { + + $qb = $this->createQueryBuilder('c'); + $qb->select('c.id as id_connector, c.label,s.name') + ->leftJoin('c.solution', 's') + ->where('c.createdBy = :user_id AND s.active = 1 AND s.'.$type.' = 1 '); + // ->setParameter('user_id', $id); + + return $qb->getQuery() + ->getResult(); + } + + // Affiche la liste des connecteurs d'un user ou tout en fonction si c'est le support + public function findListConnectorByUser($is_support,$id) { + + $qb = $this->createQueryBuilder('c'); + + $qb->innerJoin('c.solution', 'sol') + ->addSelect('sol.name solution'); + + if($is_support === false) { + $qb->where('c.createdBy = :user_id') + ->setParameter('user_id', $id); + } + + $qb->orderBy('c.id', 'DESC'); + + + return $qb->getQuery(); + + } +} diff --git a/src/Myddleware/RegleBundle/Entity/Documents.php b/src/Myddleware/RegleBundle/Entity/Documents.php new file mode 100644 index 000000000..ac7db4560 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Documents.php @@ -0,0 +1,472 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Documents + * @ORM\Table() + * @ORM\Entity + * @ORM\Table(indexes={@ORM\Index(name="index_ruleid_status", columns={"rule_id","status"})}) + */ +class Documents +{ + /** + * @var string + * + * @ORM\Column(name="id", type="string", nullable=false) + * @ORM\Id + */ + private $id; + + /** + * @var string + * + * + * @ORM\Column(name="rule_id", type="string", nullable=false) + * + */ + private $rule; + + /** + * @var datetime + * + * + * @ORM\Column(name="date_created", type="datetime", nullable=false) + * + */ + private $dateCreated; + + /** + * @var datetime + * + * @ORM\Column(name="date_modified", type="datetime", nullable=false) + * + */ + private $dateModified; + + /** + * @var integer + * + * @ORM\Column(name="created_by", type="integer", nullable=false) + * + */ + private $createdBy; + + /** + * @var integer + * + * @ORM\Column(name="modified_by", type="integer", nullable=false) + * + */ + private $modifiedBy; + + /** + * @var string + * + * + * @ORM\Column(name="status", type="string", nullable=false) + * + */ + private $status; + + /** + * @var string + * + * @ORM\Column(name="source_id", type="string", nullable=false) + * + */ + private $source; + + /** + * @var string + * + * @ORM\Column(name="target_id", type="string", nullable=false) + */ + private $target; + + /** + * @var datetime + * + * @ORM\Column(name="source_date_modified", type="datetime", nullable=false) + * + */ + private $sourceDateModified; + + /** + * @var string + * + * @ORM\Column(name="mode", type="string", length=1, nullable=false) + */ + private $mode; + + /** + * @var string + * + * @ORM\Column(name="type", type="string", length=1, nullable=false) + */ + private $type; + + /** + * @var integer + * + * @ORM\Column(name="attempt", type="integer", length=5, nullable=false) + * + */ + private $attempt; + + /** + * @var integer + * + * @ORM\Column(name="global_status", type="string", nullable=false) + * + */ + private $globalStatus; + + + /** + * Set id + * + * @param string $id + * @return Documents + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Set rule + * + * @param string $rule + * @return Documents + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set dateCreated + * + * @param \DateTime $dateCreated + * @return Documents + */ + public function setDateCreated($dateCreated) + { + $this->dateCreated = $dateCreated; + + return $this; + } + + /** + * Get dateCreated + * + * @return \DateTime + */ + public function getDateCreated() + { + return $this->dateCreated; + } + + /** + * Set dateModified + * + * @param \DateTime $dateModified + * @return Documents + */ + public function setDateModified($dateModified) + { + $this->dateModified = $dateModified; + + return $this; + } + + /** + * Get dateModified + * + * @return \DateTime + */ + public function getDateModified() + { + return $this->dateModified; + } + + /** + * Set createdBy + * + * @param integer $createdBy + * @return Documents + */ + public function setCreatedBy($createdBy) + { + $this->createdBy = $createdBy; + + return $this; + } + + /** + * Get createdBy + * + * @return integer + */ + public function getCreatedBy() + { + return $this->createdBy; + } + + /** + * Set modifiedBy + * + * @param integer $modifiedBy + * @return Documents + */ + public function setModifiedBy($modifiedBy) + { + $this->modifiedBy = $modifiedBy; + + return $this; + } + + /** + * Get modifiedBy + * + * @return integer + */ + public function getModifiedBy() + { + return $this->modifiedBy; + } + + /** + * Set status + * + * @param string $status + * @return Documents + */ + public function setStatus($status) + { + $this->status = $status; + + return $this; + } + + /** + * Get status + * + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * Set source + * + * @param string $source + * @return Documents + */ + public function setSource($source) + { + $this->source = $source; + + return $this; + } + + /** + * Get source + * + * @return string + */ + public function getSource() + { + return $this->source; + } + + /** + * Set target + * + * @param string $target + * @return Documents + */ + public function setTarget($target) + { + $this->target = $target; + + return $this; + } + + /** + * Get target + * + * @return string + */ + public function getTarget() + { + return $this->target; + } + + /** + * Set sourceDateModified + * + * @param \DateTime $sourceDateModified + * @return Documents + */ + public function setSourceDateModified($sourceDateModified) + { + $this->sourceDateModified = $sourceDateModified; + + return $this; + } + + /** + * Get sourceDateModified + * + * @return \DateTime + */ + public function getSourceDateModified() + { + return $this->sourceDateModified; + } + + /** + * Set mode + * + * @param string $mode + * @return Documents + */ + public function setMode($mode) + { + $this->mode = $mode; + + return $this; + } + + /** + * Get mode + * + * @return string + */ + public function getMode() + { + return $this->mode; + } + + /** + * Set type + * + * @param string $type + * @return Documents + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set attempt + * + * @param integer $attempt + * @return Documents + */ + public function setAttempt($attempt) + { + $this->attempt = $attempt; + + return $this; + } + + /** + * Get attempt + * + * @return integer + */ + public function getAttempt() + { + return $this->attempt; + } + + /** + * Set globalStatus + * + * @param string $globalStatus + * @return Documents + */ + public function setGlobalStatus($globalStatus) + { + $this->globalStatus = $globalStatus; + + return $this; + } + + /** + * Get globalStatus + * + * @return string + */ + public function getGlobalStatus() + { + return $this->globalStatus; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/DocumentsAudit.php b/src/Myddleware/RegleBundle/Entity/DocumentsAudit.php new file mode 100644 index 000000000..756b35fb4 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/DocumentsAudit.php @@ -0,0 +1,267 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * DocumentsAudit + * @ORM\Table() + * @ORM\HasLifecycleCallbacks() + * @ORM\Entity + */ +class DocumentsAudit +{ + /** + * @var string + * + * @ORM\Column(name="doca_id", type="string") + * @ORM\Id + */ + private $id; + + /** + * @ORM\PrePersist() + */ + public function preSave() { + $this->id = uniqid(); + } + + /** + * @var string + * + * + * @ORM\Column(name="doc_id", type="string", nullable=false) + * + */ + private $doc; + + /** + * @var datetime + * + * @ORM\Column(name="doca_modified", type="datetime", nullable=false) + * + */ + private $dateModified; + + /** + * @var integer + * + * @ORM\Column(name="doca_before", type="string", nullable=true) + * + */ + private $before; + + /** + * @var integer + * + * @ORM\Column(name="doca_after", type="string", nullable=true) + * + */ + private $after; + + /** + * @var string + * + * + * @ORM\Column(name="doca_user", type="string", nullable=false) + * + */ + private $byUser; + + + /** + * @var string + * + * + * @ORM\Column(name="doca_name", type="string", nullable=false) + * + */ + private $name; + + + + /** + * Set id + * + * @param string $id + * @return DocumentsAudit + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Set doc + * + * @param string $doc + * @return DocumentsAudit + */ + public function setDoc($doc) + { + $this->doc = $doc; + + return $this; + } + + /** + * Get doc + * + * @return string + */ + public function getDoc() + { + return $this->doc; + } + + /** + * Set dateModified + * + * @param \DateTime $dateModified + * @return DocumentsAudit + */ + public function setDateModified($dateModified) + { + $this->dateModified = $dateModified; + + return $this; + } + + /** + * Get dateModified + * + * @return \DateTime + */ + public function getDateModified() + { + return $this->dateModified; + } + + /** + * Set before + * + * @param string $before + * @return DocumentsAudit + */ + public function setBefore($before) + { + $this->before = $before; + + return $this; + } + + /** + * Get before + * + * @return string + */ + public function getBefore() + { + return $this->before; + } + + /** + * Set after + * + * @param string $after + * @return DocumentsAudit + */ + public function setAfter($after) + { + $this->after = $after; + + return $this; + } + + /** + * Get after + * + * @return string + */ + public function getAfter() + { + return $this->after; + } + + /** + * Set byUser + * + * @param string $byUser + * @return DocumentsAudit + */ + public function setByUser($byUser) + { + $this->byUser = $byUser; + + return $this; + } + + /** + * Get byUser + * + * @return string + */ + public function getByUser() + { + return $this->byUser; + } + + /** + * Set name + * + * @param string $name + * @return DocumentsAudit + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/FuncCat.php b/src/Myddleware/RegleBundle/Entity/FuncCat.php new file mode 100644 index 000000000..deae9084b --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/FuncCat.php @@ -0,0 +1,135 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; +use Doctrine\Common\Collections\ArrayCollection; + + +/** + * FuncCat + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\FuncCatRepository") + */ +class FuncCat +{ + /** + * @var integer + * + * @ORM\Column(name="fcat_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var string + * + * @ORM\Column(name="name_yml", type="string", length=100) + */ + private $name_yml; + + /** + * @var ArrayCollection $functions + * + */ + private $functions; + + /** + * Constructor + */ + public function __construct() + { + $this->functions = new \Doctrine\Common\Collections\ArrayCollection(); + } + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set name_yml + * + * @param string $nameYml + * @return FuncCat + */ + public function setNameYml($nameYml) + { + $this->name_yml = $nameYml; + + return $this; + } + + /** + * Get name_yml + * + * @return string + */ + public function getNameYml() + { + return $this->name_yml; + } + + /** + * Add functions + * + * @param \Myddleware\RegleBundle\Entity\Functions $functions + * @return FuncCat + */ + public function addFunction(\Myddleware\RegleBundle\Entity\Functions $functions) + { + $this->functions[] = $functions; + + return $this; + } + + /** + * Remove functions + * + * @param \Myddleware\RegleBundle\Entity\Functions $functions + */ + public function removeFunction(\Myddleware\RegleBundle\Entity\Functions $functions) + { + $this->functions->removeElement($functions); + } + + /** + * Get functions + * + * @return \Doctrine\Common\Collections\Collection + */ + public function getFunctions() + { + return $this->functions; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/FuncCatRepository.php b/src/Myddleware/RegleBundle/Entity/FuncCatRepository.php new file mode 100644 index 000000000..064e0cfde --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/FuncCatRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * FunctionRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class FuncCatRepository extends EntityRepository +{ +} diff --git a/src/Myddleware/RegleBundle/Entity/Functions.php b/src/Myddleware/RegleBundle/Entity/Functions.php new file mode 100644 index 000000000..cd44b0133 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Functions.php @@ -0,0 +1,121 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Functions + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\FunctionsRepository") + */ +class Functions +{ + /** + * @var integer + * + * @ORM\Column(name="func_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + + /** + * @var FuncCat $FuncCat + * + * @ORM\ManyToOne(targetEntity="FuncCat") + * @ORM\JoinColumn(name="fcat_id", referencedColumnName="fcat_id") + * + */ + private $categorieId; + + + /** + * @var string + * + * @ORM\Column(name="func_name", type="string", length=20) + */ + private $name; + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set name + * + * @param string $name + * @return Functions + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set categorieId + * + * @param \Myddleware\RegleBundle\Entity\FuncCat $categorieId + * @return Functions + */ + public function setCategorieId(\Myddleware\RegleBundle\Entity\FuncCat $categorieId) + { + $this->categorieId = $categorieId; + + return $this; + } + + /** + * Get categorieId + * + * @return \Myddleware\RegleBundle\Entity\FuncCat + */ + public function getCategorieId() + { + return $this->categorieId; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/FunctionsRepository.php b/src/Myddleware/RegleBundle/Entity/FunctionsRepository.php new file mode 100644 index 000000000..9a3a51203 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/FunctionsRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * FunctionRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class FunctionsRepository extends EntityRepository +{ +} diff --git a/src/Myddleware/RegleBundle/Entity/Job.php b/src/Myddleware/RegleBundle/Entity/Job.php new file mode 100644 index 000000000..c992b0d11 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Job.php @@ -0,0 +1,385 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Job + * @ORM\Table() + * @ORM\Entity + * @ORM\Table(indexes={ + * @ORM\Index(name="index_job_status", columns={"job_status"}) + *}) + */ +class Job +{ + /** + * @var string + * + * @ORM\Column(name="job_id", type="string", length=255, nullable=false) + * @ORM\Id + */ + private $id; + + /** + * @var string + * + * + * @ORM\Column(name="job_status", type="string", length=50, nullable=false) + * + */ + private $status; + + /** + * @var string + * + * + * @ORM\Column(name="job_param", type="string", length=50, nullable=false) + * + */ + private $param; + + /** + * @var datetime + * + * @ORM\Column(name="job_begin", type="datetime", nullable=false) + * + */ + private $begin; + + /** + * @var datetime + * + * @ORM\Column(name="job_end", type="datetime", nullable=false) + * + */ + private $end; + + + /** + * @var string + * + * @ORM\Column(name="job_message", type="text", nullable=false) + * + */ + private $message; + + + /** + * @var integer + * + * @ORM\Column(name="job_open", type="integer", length=6, nullable=false) + * + */ + private $open; + + /** + * @var integer + * + * @ORM\Column(name="job_close", type="integer", length=6, nullable=false) + * + */ + private $close; + + /** + * @var integer + * + * @ORM\Column(name="job_cancel", type="integer", length=6, nullable=false) + * + */ + private $cancel; + + /** + * @var boolean + * + * @ORM\Column(name="job_manual", type="boolean", nullable=false) + * + */ + private $manual; + + /** + * @var integer + * + * @ORM\Column(name="job_error", type="integer", length=6, nullable=false) + * + */ + private $error; + + + /** + * Set id + * + * @param string $id + * @return Job + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Set status + * + * @param string $status + * @return Job + */ + public function setStatus($status) + { + $this->status = $status; + + return $this; + } + + /** + * Get status + * + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * Set param + * + * @param string $param + * @return Job + */ + public function setParam($param) + { + $this->param = $param; + + return $this; + } + + /** + * Get param + * + * @return string + */ + public function getParam() + { + return $this->param; + } + + /** + * Set begin + * + * @param \DateTime $begin + * @return Job + */ + public function setBegin($begin) + { + $this->begin = $begin; + + return $this; + } + + /** + * Get begin + * + * @return \DateTime + */ + public function getBegin() + { + return $this->begin; + } + + /** + * Set end + * + * @param \DateTime $end + * @return Job + */ + public function setEnd($end) + { + $this->end = $end; + + return $this; + } + + /** + * Get end + * + * @return \DateTime + */ + public function getEnd() + { + return $this->end; + } + + /** + * Set message + * + * @param string $message + * @return Job + */ + public function setMessage($message) + { + $this->message = $message; + + return $this; + } + + /** + * Get message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Set open + * + * @param integer $open + * @return Job + */ + public function setOpen($open) + { + $this->open = $open; + + return $this; + } + + /** + * Get open + * + * @return integer + */ + public function getOpen() + { + return $this->open; + } + + /** + * Set close + * + * @param integer $close + * @return Job + */ + public function setClose($close) + { + $this->close = $close; + + return $this; + } + + /** + * Get close + * + * @return integer + */ + public function getClose() + { + return $this->close; + } + + /** + * Set cancel + * + * @param integer $cancel + * @return Job + */ + public function setCancel($cancel) + { + $this->cancel = $cancel; + + return $this; + } + + /** + * Get cancel + * + * @return integer + */ + public function getCancel() + { + return $this->cancel; + } + + /** + * Set manual + * + * @param integer $manual + * @return Job + */ + public function setManual($manual) + { + $this->manual = $manual; + + return $this; + } + + /** + * Get manual + * + * @return integer + */ + public function getManual() + { + return $this->manual; + } + + /** + * Set error + * + * @param integer $error + * @return Job + */ + public function setError($error) + { + $this->error = $error; + + return $this; + } + + /** + * Get error + * + * @return integer + */ + public function getError() + { + return $this->error; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/Log.php b/src/Myddleware/RegleBundle/Entity/Log.php new file mode 100644 index 000000000..4583cbda9 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Log.php @@ -0,0 +1,280 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * Log + * @ORM\Table() + * @ORM\Entity + * @ORM\Table(indexes={ + * @ORM\Index(name="index_doc_id", columns={"doc_id"}), + * @ORM\Index(name="index_job_id", columns={"job_id"}) + *}) + */ +class Log +{ + /** + * @var integer + * + * @ORM\Column(name="log_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var datetime + * + * + * @ORM\Column(name="log_created", type="datetime", nullable=false) + * + */ + private $dateCreated; + + + /** + * @var string + * + * @ORM\Column(name="log_type", type="string", length=5, nullable=false) + * + */ + private $type; + + /** + * @var text + * + * @ORM\Column(name="log_msg", type="text", nullable=false) + * + */ + private $message; + + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string", length=255, nullable=false) + * + */ + private $rule; + + /** + * @var string + * + * @ORM\Column(name="doc_id", type="string", length=255, nullable=false) + * + */ + private $document; + + /** + * @var string + * + * @ORM\Column(name="ref_doc_id", type="string", length=255, nullable=false) + * + */ + private $ref; + + /** + * @var string + * + * @ORM\Column(name="job_id", type="string", length=255, nullable=false) + * + */ + private $job; + + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set dateCreated + * + * @param \DateTime $dateCreated + * @return Log + */ + public function setDateCreated($dateCreated) + { + $this->dateCreated = $dateCreated; + + return $this; + } + + /** + * Get dateCreated + * + * @return \DateTime + */ + public function getDateCreated() + { + return $this->dateCreated; + } + + /** + * Set type + * + * @param string $type + * @return Log + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set message + * + * @param string $message + * @return Log + */ + public function setMessage($message) + { + $this->message = $message; + + return $this; + } + + /** + * Get message + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * Set rule + * + * @param string $rule + * @return Log + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set document + * + * @param string $document + * @return Log + */ + public function setDocument($document) + { + $this->document = $document; + + return $this; + } + + /** + * Get document + * + * @return string + */ + public function getDocument() + { + return $this->document; + } + + /** + * Set ref + * + * @param string $ref + * @return Log + */ + public function setRef($ref) + { + $this->ref = $ref; + + return $this; + } + + /** + * Get ref + * + * @return string + */ + public function getRef() + { + return $this->ref; + } + + /** + * Set job + * + * @param string $job + * @return Log + */ + public function setJob($job) + { + $this->job = $job; + + return $this; + } + + /** + * Get job + * + * @return string + */ + public function getJob() + { + return $this->job; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/Rule.php b/src/Myddleware/RegleBundle/Entity/Rule.php new file mode 100644 index 000000000..e6c0e47ab --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Rule.php @@ -0,0 +1,495 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; +use Gedmo\Mapping\Annotation as Gedmo; // slug +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; // unique + +/** + * Rule + * + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\RuleRepository") + * @ORM\HasLifecycleCallbacks() + * @ORM\Table(name="Rule", + * uniqueConstraints={ + * @ORM\UniqueConstraint( + * name="unique_namev", + * columns={"rule_name", "rule_version"} + * ) + * } + * ) + * + * @UniqueEntity(fields={"rule_name", "rule_version"}, message="my.unique_namev.unique") + * @ORM\Table(indexes={@ORM\Index(name="Krule_name", columns={"rule_name"})}) + */ + +class Rule +{ + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string") + * @ORM\Id + */ + private $id; + + /** + * @ORM\PrePersist() + */ + public function preSave() { + $this->id = uniqid(); + } + + /** + * @var Connector $connectorSource + * + * @ORM\ManyToOne(targetEntity="Connector") + * @ORM\JoinColumn(name="conn_id_source", referencedColumnName="conn_id") + * + */ + private $connectorSource; + + /** + * @var Connector $connectorTarget + * + * @ORM\ManyToOne(targetEntity="Connector") + * @ORM\JoinColumn(name="conn_id_target", referencedColumnName="conn_id") + * + */ + private $connectorTarget; + + /** + * @var datetime + * + * + * @ORM\Column(name="rule_date_created", type="datetime", nullable=false) + * + */ + private $dateCreated; + + /** + * @var datetime + * + * @ORM\Column(name="rule_date_modified", type="datetime", nullable=false) + * + */ + private $dateModified; + + /** + * @var integer + * + * @ORM\Column(name="rule_created_by", type="integer", nullable=false) + * + */ + private $createdBy; + + /** + * @var integer + * + * @ORM\Column(name="rule_modified_by", type="integer", nullable=false) + * + */ + private $modifiedBy; + + /** + * @var string + * + * @ORM\Column(name="rule_module_source", type="string", nullable=false) + * + */ + private $moduleSource; + + /** + * @var string + * + * @ORM\Column(name="rule_module_target", type="string", nullable=false) + */ + private $moduleTarget; + + + /** + * @var boolean + * + * @ORM\Column(name="rule_active", type="boolean", nullable=false) + * + */ + private $active; + + /** + * @var boolean + * + * @ORM\Column(name="rule_deleted", type="boolean", nullable=false) + * + */ + private $deleted; + + /** + * @var string + * + * @ORM\Column(name="rule_name", type="string", length=50, nullable=false) + * + */ + private $name; + + /** + * @Gedmo\Slug(fields={"name"}, separator="_") + * @ORM\Column(length=50, nullable=false, name="rule_name_slug") + */ + private $nameSlug; + + + /** + * @var string + * + * @ORM\Column(name="rule_version", type="string", length=3, nullable=false) + * + */ + private $version; + + + /** + * Set id + * + * @param string $id + * @return Rule + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Set dateCreated + * + * @param \DateTime $dateCreated + * @return Rule + */ + public function setDateCreated($dateCreated) + { + $this->dateCreated = $dateCreated; + + return $this; + } + + /** + * Get dateCreated + * + * @return \DateTime + */ + public function getDateCreated() + { + return $this->dateCreated; + } + + /** + * Set dateModified + * + * @param \DateTime $dateModified + * @return Rule + */ + public function setDateModified($dateModified) + { + $this->dateModified = $dateModified; + + return $this; + } + + /** + * Get dateModified + * + * @return \DateTime + */ + public function getDateModified() + { + return $this->dateModified; + } + + /** + * Set createdBy + * + * @param integer $createdBy + * @return Rule + */ + public function setCreatedBy($createdBy) + { + $this->createdBy = $createdBy; + + return $this; + } + + /** + * Get createdBy + * + * @return integer + */ + public function getCreatedBy() + { + return $this->createdBy; + } + + /** + * Set modifiedBy + * + * @param integer $modifiedBy + * @return Rule + */ + public function setModifiedBy($modifiedBy) + { + $this->modifiedBy = $modifiedBy; + + return $this; + } + + /** + * Get modifiedBy + * + * @return integer + */ + public function getModifiedBy() + { + return $this->modifiedBy; + } + + /** + * Set moduleSource + * + * @param string $moduleSource + * @return Rule + */ + public function setModuleSource($moduleSource) + { + $this->moduleSource = $moduleSource; + + return $this; + } + + /** + * Get moduleSource + * + * @return string + */ + public function getModuleSource() + { + return $this->moduleSource; + } + + /** + * Set moduleTarget + * + * @param string $moduleTarget + * @return Rule + */ + public function setModuleTarget($moduleTarget) + { + $this->moduleTarget = $moduleTarget; + + return $this; + } + + /** + * Get moduleTarget + * + * @return string + */ + public function getModuleTarget() + { + return $this->moduleTarget; + } + + /** + * Set active + * + * @param integer $active + * @return Rule + */ + public function setActive($active) + { + $this->active = $active; + + return $this; + } + + /** + * Get active + * + * @return integer + */ + public function getActive() + { + return $this->active; + } + + /** + * Set deleted + * + * @param integer $deleted + * @return Rule + */ + public function setDeleted($deleted) + { + $this->deleted = $deleted; + + return $this; + } + + /** + * Get deleted + * + * @return integer + */ + public function getDeleted() + { + return $this->deleted; + } + + /** + * Set name + * + * @param string $name + * @return Rule + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set nameSlug + * + * @param string $nameSlug + * @return Rule + */ + public function setNameSlug($nameSlug) + { + $this->nameSlug = $nameSlug; + + return $this; + } + + /** + * Get nameSlug + * + * @return string + */ + public function getNameSlug() + { + return $this->nameSlug; + } + + /** + * Set version + * + * @param string $version + * @return Rule + */ + public function setVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * Get version + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Set connectorSource + * + * @param \Myddleware\RegleBundle\Entity\Connector $connectorSource + * @return Rule + */ + public function setConnectorSource(\Myddleware\RegleBundle\Entity\Connector $connectorSource) + { + $this->connectorSource = $connectorSource; + + return $this; + } + + /** + * Get connectorSource + * + * @return \Myddleware\RegleBundle\Entity\Connector + */ + public function getConnectorSource() + { + return $this->connectorSource; + } + + /** + * Set connectorTarget + * + * @param \Myddleware\RegleBundle\Entity\Connector $connectorTarget + * @return Rule + */ + public function setConnectorTarget(\Myddleware\RegleBundle\Entity\Connector $connectorTarget) + { + $this->connectorTarget = $connectorTarget; + + return $this; + } + + /** + * Get connectorTarget + * + * @return \Myddleware\RegleBundle\Entity\Connector + */ + public function getConnectorTarget() + { + return $this->connectorTarget; + } +} diff --git a/src/Myddleware/RegleBundle/Entity/RuleFields.php b/src/Myddleware/RegleBundle/Entity/RuleFields.php new file mode 100644 index 000000000..139dd9970 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleFields.php @@ -0,0 +1,182 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * RuleFields + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\RuleFieldsRepository") + * @ORM\Table(indexes={@ORM\Index(name="Krule_id", columns={"rule_id"})}) + */ +class RuleFields +{ + /** + * @var integer + * + * @ORM\Column(name="rulef_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string") + */ + private $rule; + + + /** + * @var string + * + * @ORM\Column(name="rulef_target_field_name", type="text", nullable=false) + * + */ + private $target; + + /** + * @var string + * + * @ORM\Column(name="rulef_source_field_name", type="text", nullable=false) + * + */ + private $source; + + /** + * @var string + * + * @ORM\Column(name="rulef_formula", type="text", nullable=true) + * + */ + private $formula; + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set rule + * + * @param string $rule + * @return RuleFields + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set target + * + * @param string $target + * @return RuleFields + */ + public function setTarget($target) + { + $this->target = $target; + + return $this; + } + + /** + * Get target + * + * @return string + */ + public function getTarget() + { + return $this->target; + } + + /** + * Set source + * + * @param string $source + * @return RuleFields + */ + public function setSource($source) + { + $this->source = $source; + + return $this; + } + + /** + * Get source + * + * @return string + */ + public function getSource() + { + return $this->source; + } + + /** + * Set formula + * + * @param string $formula + * @return RuleFields + */ + public function setFormula($formula) + { + $this->formula = $formula; + + return $this; + } + + /** + * Get formula + * + * @return string + */ + public function getFormula() + { + return $this->formula; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/RuleFieldsRepository.php b/src/Myddleware/RegleBundle/Entity/RuleFieldsRepository.php new file mode 100644 index 000000000..77fad9ef9 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleFieldsRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * ConnectorParamsRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class RuleFieldsRepository extends EntityRepository +{ +} diff --git a/src/Myddleware/RegleBundle/Entity/RuleFilters.php b/src/Myddleware/RegleBundle/Entity/RuleFilters.php new file mode 100644 index 000000000..c9552ebe8 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleFilters.php @@ -0,0 +1,183 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * RuleFilters + * @ORM\Entity + * @ORM\HasLifecycleCallbacks() + * @ORM\Table(indexes={@ORM\Index(name="Krule_id", columns={"rule_id"})}) + */ + +class RuleFilters +{ + /** + * @var integer + * + * @ORM\Column(name="rfi_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string", length=255, nullable=false) + * + */ + private $rule; + + /** + * @var string + * + * @ORM\Column(name="rfi_target", type="string", length=255, nullable=false) + * + */ + private $target; + + /** + * @var string + * + * @ORM\Column(name="rfi_type", type="string", length=255, nullable=false) + * + */ + private $type; + + /** + * @var string + * + * @ORM\Column(name="rfi_value", type="string", length=255, nullable=false) + * + */ + private $value; + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set rule + * + * @param string $rule + * @return RuleFilters + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set target + * + * @param string $target + * @return RuleFilters + */ + public function setTarget($target) + { + $this->target = $target; + + return $this; + } + + /** + * Get target + * + * @return string + */ + public function getTarget() + { + return $this->target; + } + + /** + * Set type + * + * @param string $type + * @return RuleFilters + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set value + * + * @param string $value + * @return RuleFilters + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get value + * + * @return string + */ + public function getValue() + { + return $this->value; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/RuleOrder.php b/src/Myddleware/RegleBundle/Entity/RuleOrder.php new file mode 100644 index 000000000..41d81efd7 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleOrder.php @@ -0,0 +1,100 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * RuleOrder + * @ORM\Table() + * @ORM\Entity + */ +class RuleOrder +{ + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string", length=100) + * @ORM\Id + */ + private $rule; + + /** + * @var string + * + * + * @ORM\Column(name="rod_order", type="integer", length=3, nullable=false) + * + */ + private $order; + + + /** + * Set rule + * + * @param string $rule + * @return RuleOrder + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set order + * + * @param integer $order + * @return RuleOrder + */ + public function setOrder($order) + { + $this->order = $order; + + return $this; + } + + /** + * Get order + * + * @return integer + */ + public function getOrder() + { + return $this->order; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/RuleParams.php b/src/Myddleware/RegleBundle/Entity/RuleParams.php new file mode 100644 index 000000000..927c5110e --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleParams.php @@ -0,0 +1,150 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * RuleParams + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\RuleParamsRepository") + * @ORM\Table(indexes={@ORM\Index(name="Krule_id", columns={"rule_id"})}) + */ +class RuleParams +{ + /** + * @var integer + * + * @ORM\Column(name="rulep_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string", nullable=false) + */ + private $rule; + + /** + * @var string + * + * @ORM\Column(name="rulep_name", type="string", length=100, nullable=false) + * + */ + private $name; + + /** + * @var string + * + * @ORM\Column(name="rulep_value", type="text", nullable=false) + * + */ + private $value; + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set rule + * + * @param string $rule + * @return RuleParams + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set name + * + * @param string $name + * @return RuleParams + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set value + * + * @param string $value + * @return RuleParams + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get value + * + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/src/Myddleware/RegleBundle/Entity/RuleParamsRepository.php b/src/Myddleware/RegleBundle/Entity/RuleParamsRepository.php new file mode 100644 index 000000000..090557e1f --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleParamsRepository.php @@ -0,0 +1,38 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * ConnectorParamsRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class RuleParamsRepository extends EntityRepository +{ +} diff --git a/src/Myddleware/RegleBundle/Entity/RuleRelationShips.php b/src/Myddleware/RegleBundle/Entity/RuleRelationShips.php new file mode 100644 index 000000000..038b1b423 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleRelationShips.php @@ -0,0 +1,186 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; + +/** + * RuleRelationShips + * + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\RuleRelationShipsRepository") + * @ORM\HasLifecycleCallbacks() + * @ORM\Table(indexes={@ORM\Index(name="Krule_id", columns={"rule_id"})}) + */ + +class RuleRelationShips +{ + /** + * @var string + * + * @ORM\Column(name="rrs_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + + /** + * @var string + * + * @ORM\Column(name="rule_id", type="string", length=100, nullable=false) + * + */ + private $rule; + + /** + * @var string + * + * @ORM\Column(name="rrs_field_name_source", type="string", length=100, nullable=false) + * + */ + private $fieldNameSource; + + /** + * @var string + * + * @ORM\Column(name="rrs_field_name_target", type="string", length=100, nullable=false) + * + */ + private $fieldNameTarget; + + + /** + * @var integer + * + * @ORM\Column(name="rrs_field_id", type="string", length=100, nullable=true) + * + */ + private $fieldId; + + + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set rule + * + * @param string $rule + * @return RuleRelationShips + */ + public function setRule($rule) + { + $this->rule = $rule; + + return $this; + } + + /** + * Get rule + * + * @return string + */ + public function getRule() + { + return $this->rule; + } + + /** + * Set fieldNameSource + * + * @param string $fieldNameSource + * @return RuleRelationShips + */ + public function setFieldNameSource($fieldNameSource) + { + $this->fieldNameSource = $fieldNameSource; + + return $this; + } + + /** + * Get fieldNameSource + * + * @return string + */ + public function getFieldNameSource() + { + return $this->fieldNameSource; + } + + /** + * Set fieldNameTarget + * + * @param string $fieldNameTarget + * @return RuleRelationShips + */ + public function setFieldNameTarget($fieldNameTarget) + { + $this->fieldNameTarget = $fieldNameTarget; + + return $this; + } + + /** + * Get fieldNameTarget + * + * @return string + */ + public function getFieldNameTarget() + { + return $this->fieldNameTarget; + } + + /** + * Set fieldId + * + * @param string $fieldId + * @return RuleRelationShips + */ + public function setFieldId($fieldId) + { + $this->fieldId = $fieldId; + + return $this; + } + + /** + * Get fieldId + * + * @return string + */ + public function getFieldId() + { + return $this->fieldId; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/RuleRelationShipsRepository.php b/src/Myddleware/RegleBundle/Entity/RuleRelationShipsRepository.php new file mode 100644 index 000000000..984021ed2 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleRelationShipsRepository.php @@ -0,0 +1,39 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * SolutionRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class RuleRelationShipsRepository extends EntityRepository +{ + +} diff --git a/src/Myddleware/RegleBundle/Entity/RuleRepository.php b/src/Myddleware/RegleBundle/Entity/RuleRepository.php new file mode 100644 index 000000000..eb8bd162c --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/RuleRepository.php @@ -0,0 +1,108 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query\ResultSetMapping; + + +/** + * ConnectorParamsRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class RuleRepository extends EntityRepository +{ + // Retourne le nombre de règle pour un user + public function findCountAllRuleByUser($id) + { + return $this->createQueryBuilder('r') + ->select('COUNT(r)') + ->where('r.createdBy = :user_id') + ->setParameter('user_id', $id) + ->getQuery() + ->getSingleScalarResult(); + } + + // Retourne toutes les règles d'un user + public function findListRuleByUser($is_support,$id){ + + $sql = $this->createQueryBuilder('r') + ->innerJoin('r.connectorSource', 'cs') + ->innerJoin('r.connectorTarget', 'ct') + ->innerJoin('cs.solution', 'Solution_source') + ->innerJoin('ct.solution', 'Solution_target') + ->addSelect('r.id') + ->addSelect('r.dateCreated') + ->addSelect('r.name') + ->addSelect('r.active') + ->addSelect('r.version') + ->addSelect('r.nameSlug') + ->addSelect('r.createdBy') + ->addSelect('cs.label lbl_source') + ->addSelect('ct.label lbl_target') + ->addSelect('Solution_source.name solution_source') + ->addSelect('Solution_target.name solution_target') + ->addOrderBy('r.active', 'DESC') + ->addOrderBy('cs.label', 'DESC') + ->addOrderBy('ct.label', 'DESC') + ->addOrderBy('r.name', 'ASC'); + + // si ce n'est pas le support alors on affecte l'id client sinon on affiche tout + if($is_support == false) { + $sql->where('r.createdBy = :user_id AND r.deleted = 0') + ->setParameter('user_id', $id); + } + else { + $sql->where('r.deleted = 0'); + } + + return $sql->getQuery(); + + } + + // Infos connecteurs et solution d'une règle + public function infosConnectorByRule($id){ + + return $this->createQueryBuilder('r') + ->innerJoin('r.connectorSource', 'cs') + ->innerJoin('r.connectorTarget', 'ct') + ->innerJoin('cs.solution', 'Solution_source') + ->innerJoin('ct.solution', 'Solution_target') + ->addSelect('cs.label lbl_source') + ->addSelect('ct.label lbl_target') + ->addSelect('cs.id id_source') + ->addSelect('ct.id id_target') + ->addSelect('Solution_source.name solution_source') + ->addSelect('Solution_target.name solution_target') + ->where('r.id = :rule_id') + ->setParameter('rule_id', $id) + ->getQuery() + ->getResult(); + } + +} diff --git a/src/Myddleware/RegleBundle/Entity/Solution.php b/src/Myddleware/RegleBundle/Entity/Solution.php new file mode 100644 index 000000000..e69353f31 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/Solution.php @@ -0,0 +1,230 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; +use Doctrine\Common\Collections\ArrayCollection; + + +/** + * Solution + * + * @ORM\Table() + * @ORM\Entity(repositoryClass="Myddleware\RegleBundle\Entity\SolutionRepository") + */ +class Solution +{ + /** + * @var integer + * + * @ORM\Column(name="sol_id", type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + + /** + * @var string + * + * @ORM\Column(name="sol_name", type="string", length=20,nullable=false) + */ + private $name; + + + /** + * @var string + * + * @ORM\Column(name="sol_active", type="integer", length=1,nullable=false) + */ + private $active; + + /** + * @var integer + * + * @ORM\Column(name="sol_source", type="integer", length=1,nullable=false) + */ + private $source; + + /** + * @var integer + * + * @ORM\Column(name="sol_target", type="integer", length=1,nullable=false) + */ + private $target; + + /** + * @var ArrayCollection $connector + * @ORM\OneToMany(targetEntity="Connector", mappedBy="solution", cascade={"persist", "remove", "merge"}) + * + */ + private $connector; + + + + /** + * Constructor + */ + public function __construct() + { + $this->connector = new \Doctrine\Common\Collections\ArrayCollection(); + } + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set name + * + * @param string $name + * @return Solution + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set active + * + * @param integer $active + * @return Solution + */ + public function setActive($active) + { + $this->active = $active; + + return $this; + } + + /** + * Get active + * + * @return integer + */ + public function getActive() + { + return $this->active; + } + + /** + * Set source + * + * @param integer $source + * @return Solution + */ + public function setSource($source) + { + $this->source = $source; + + return $this; + } + + /** + * Get source + * + * @return integer + */ + public function getSource() + { + return $this->source; + } + + /** + * Set target + * + * @param integer $target + * @return Solution + */ + public function setTarget($target) + { + $this->target = $target; + + return $this; + } + + /** + * Get target + * + * @return integer + */ + public function getTarget() + { + return $this->target; + } + + /** + * Add connector + * + * @param \Myddleware\RegleBundle\Entity\Connector $connector + * @return Solution + */ + public function addConnector(\Myddleware\RegleBundle\Entity\Connector $connector) + { + $this->connector[] = $connector; + + return $this; + } + + /** + * Remove connector + * + * @param \Myddleware\RegleBundle\Entity\Connector $connector + */ + public function removeConnector(\Myddleware\RegleBundle\Entity\Connector $connector) + { + $this->connector->removeElement($connector); + } + + /** + * Get connector + * + * @return \Doctrine\Common\Collections\Collection + */ + public function getConnector() + { + return $this->connector; + } +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Entity/SolutionRepository.php b/src/Myddleware/RegleBundle/Entity/SolutionRepository.php new file mode 100644 index 000000000..db6043140 --- /dev/null +++ b/src/Myddleware/RegleBundle/Entity/SolutionRepository.php @@ -0,0 +1,116 @@ +. +*********************************************************************************/ + +namespace Myddleware\RegleBundle\Entity; + +use Doctrine\ORM\EntityRepository; + +/** + * SolutionRepository + * + * This class was generated by the Doctrine ORM. Add your own custom + * repository methods below. + */ +class SolutionRepository extends EntityRepository +{ + + // Liste des solutions actives + public function solutionActive() + { + $qb = $this->_em->createQueryBuilder(); + + $qb->select('s') + ->from('RegleBundle:Solution', 's') + ->where('s.active = :active') + ->setParameter('active', 1); + + return $qb->getQuery() + ->getResult(); + + // getArrayResult() + // getScalarResult() + // getOneOrNullResult() + // getSingleResult() + // getSingleScalarResult() + // execute() + + } + + + // Liste des solutions en fonction des connecteurs + public function solutionConnector($type,$is_support,$id) + { + $qb = $this->_em->createQueryBuilder(); + + $field = (($type == 'target') ? 'target' : 'source'); + + $qb->select('s','c') + ->from('RegleBundle:Solution', 's') + ->innerJoin('s.connector', 'c'); + + // si ce n'est pas le support alors on affecte l'id client sinon on affiche tout + // On affiche uniquement les connecteurs du user + if($is_support === false) { + $qb->where('s.active = :active AND s.'.$field.' = :type AND c.createdBy = :user_id') + ->setParameter('active', 1) + ->setParameter('type', 1) + ->setParameter('user_id', $id); + } + else { + $qb->where('s.active = :active AND s.'.$field.' = :type') + ->setParameter('active', 1) + ->setParameter('type', 1); + } + + + $qb ->groupBy('s.name') + ->orderBy('s.name', 'ASC'); + + return $qb->getQuery() + ->getResult(); + + } + + + // Liste des solutions en fonction des types + public function solutionConnectorType($type) + { + $qb = $this->_em->createQueryBuilder(); + + $field = (($type == 'target') ? 'target' : 'source'); + + $qb->select('s') + ->from('RegleBundle:Solution', 's') + ->where('s.active = :active AND s.'.$field.' = :type') + ->setParameter('active', 1) + ->setParameter('type', 1) + ->groupBy('s.name') + ->orderBy('s.name', 'ASC'); + return $qb->getQuery() + ->getResult(); + } + + +} diff --git a/src/Myddleware/RegleBundle/RegleBundle.php b/src/Myddleware/RegleBundle/RegleBundle.php new file mode 100644 index 000000000..71beeee03 --- /dev/null +++ b/src/Myddleware/RegleBundle/RegleBundle.php @@ -0,0 +1,9 @@ +. +*********************************************************************************/ + +#user_account .onoffswitch { + position: relative; width: 170px; + -webkit-user-select:none; -moz-user-select:none; -ms-user-select: none; + top: 5px; right: 5px; +} +#user_account .onoffswitch-checkbox { + display: none; +} +#user_account .onoffswitch-label { + display: block; overflow: hidden; cursor: pointer; + border: 2px solid #999999; border-radius: 0px; +} +#user_account .onoffswitch-inner { + display: block; width: 200%; margin-left: -100%; + -moz-transition: margin 0.3s ease-in 0s; -webkit-transition: margin 0.3s ease-in 0s; + -o-transition: margin 0.3s ease-in 0s; transition: margin 0.3s ease-in 0s; +} +#user_account .onoffswitch-inner:before, .onoffswitch-inner:after { + display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 26px; + font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold; + -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; + border: 2px solid transparent; + background-clip: padding-box; +} +#user_account .onoffswitch-inner:before { + padding-left: 10px; + background-color: #0069B4; color: #FFFFFF; +} +#user_account .onoffswitch-inner:after { + padding-right: 10px; + background-color: #CCCCCC; color: #333333; + text-align: right; +} +#user_account .onoffswitch-switch { + display: block; width: 25px; height: 34px; margin: 0px; + background: #444446; + position: absolute; top: 0; bottom: 0; right: 145px; + -moz-transition: all 0.3s ease-in 0s; -webkit-transition: all 0.3s ease-in 0s; + -o-transition: all 0.3s ease-in 0s; transition: all 0.3s ease-in 0s; +} +#user_account .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { + margin-left: 0; +} +#user_account .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { + right: 0px; +} + +#user_account{ + width: 90%; + padding-top: 25px; + padding-bottom: 25px; + margin-left: 5%; +} +#user_account ul, li{ + text-decoration: none; + list-style: none; + padding-left: 0px; +} +#user_info{ + margin-bottom: 25px; +} +.user_info_li{ + height: 40px; + font-size: 1.2em; + margin-top: 10px; +} +.user_info_li > label{ + height: 100%; + color: #0065AB; + font-weight: bold; + margin-right: 20px; +} +.user_info_li > input{ + height: 100%; + width: 250px; + color: #999219; +} +.user_info_li > input:disabled{ + background-color: white; + border: none; +} +#user_tickets{ + width: 100%; +} +#user_tickets > .listepager{ + width: 100%; +} +#user_tickets th { + background: #ECECEC; + height: 20px; + padding: 2px; + text-align: center; + min-width: 120px; +} +#user_tickets tr { + border-bottom: 1px solid #ccc; +} +#user_tickets td { + text-align: center; +} +#user_tickets td.lft { + text-align: center; +} +#user_tickets td.lft > a{ + color: black; +} +#user_tickets .vignette { + width: 50px; +} +#user_account .user_account_popup_content { + color: #C6BD20; + font-weight: bold; + text-align: center; + position: absolute; + width: 400px; + height: 300px; + padding: 20px; + padding-top: 110px; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.8); + top: 50px; + left: 500px; + border-radius: 20px; + box-shadow: 1px 1px 12px #555; + z-index: 100; +} +#user_account .user_account_popup_content img{ + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.9); + border-radius: 15px; +} +#user_account .user_account_popup { + position: absolute; + background-color: rgba(0, 0, 0, 0.1); + top: 0; + left: 0; + z-index: 99; +} +#user_account .selected{ + color: #292707; + background-color: #C6BD20; + border-color: #999219; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/css/animation.css b/src/Myddleware/RegleBundle/Resources/public/css/animation.css new file mode 100644 index 000000000..c05eccbe2 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/animation.css @@ -0,0 +1,459 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +* { + margin: 0; + padding: 0; + font-size: 1em; + text-decoration: none; + border: none; + list-style: none; + outline: none; +} + +#animation .loader-source, #animation .loader-cible { + width: 43px; + height: 11px; + min-height:11px; + min-width:43px; + display: inline-block; + margin: 0 15px; +} + +#animation #connector-source-error, #animation #connector-cible-error, +#animation #connector-source-success, #animation #connector-cible-success { + width: 300px; + max-width: 300px; + height: auto; + word-wrap: break-word; +} + +#animation #connector-source-error, #animation #connector-source-success { + float: left; +} + +#animation #connector-cible-error, #animation #connector-cible-success { + float: right; +} + +#animation { + max-width: 1280px; + margin: auto; +} + +#animation > .animation-content{ + display: inline-block; + vertical-align: top; +} + +#animation a { + color: #067dd7; +} +#animation a:hover { + text-decoration: underline; +} + +#animation .animation-content-left{ + width: 15%; + height: 100%; + padding-left: 5px; +} + +#animation .animation-position-text-source{ + position: relative; + top: 180px; + left: 72px; +} + + +#animation .animation-content-right{ + width: 15%; + height: 100%; + padding-right: 5px; +} + +#animation .animation-content-right > div{ + float: right; +} + +#animation .animation-position-text-cible{ + position: relative; + top: 260px; + right: 72px; +} + +#animation .animation-content-left-toggle-btn{ + display: none; +} + +#animation .animation-content-right-toggle-btn{ + display: none; +} + +#animation .animation-workspace{ + width: 69%; + height: 100%; + margin-top: 5%; +} + +#animation .animation-workspace-title{ + text-align: center; + font-weight: normal; + text-shadow: 0px 0px 1px #AAA; + color: #424140; +} + +#animation .animation-center{ + margin: auto; +} + +#animation .animation-workspace-container{ + width: 100%; + height: 200px; +} + +#animation .animation-workspace-content{ + width: 20%; + height: 100%; + float: left; +} + +#animation .animation-workspace img{ + background: #EEE; + border: 2px solid #AAA; + border-radius: 10px; + opacity: 0.9; + display: inline-block; + height: 65px; + width: 65px; +} + +#animation-myddleware-div div{ + height: 150px; + width: 150px; +} + +#animation-myddleware-div .animation-myddleware-logo{ + border: none; + padding: none; + height: 105px; + margin: auto; + margin-top: 50px; +} + +#animation-myddleware-back-2 > .animation-myddleware-logo { + float: left; + width: 0px; +} + +#animation-myddleware-back-3 > .animation-myddleware-logo { + float: right; + margin-right: 45px; + width: 0px; +} + +#animation .animation-puce > ul{ + width: 120px; + height: 120px; + margin: auto; + margin-top: 10%; + padding-top: 50px; + text-align: center; +} + +#animation select{ + padding: 5px; + /*width: 85px;*/ + overflow: hidden; + text-align: left; + font-size: 1em; + border-radius: 1px; + -webkit-box-shadow: rgba(0,0,0,.6) -1px -2px 2px; + -moz-box-shadow: rgba(0,0,0,.6) 0px 2px 2px; + background:white; +} + +/* PROGRESS-BAR */ + +#animation .animation-bar-left { + height: 20px; + width: 80%; + border-radius: 4px; + background: #DDDBDE; + border: 1px solid #AAA; + overflow: hidden; +} +#animation .animation-bar-left .progress-bar { + float: left; + border-radius: 4px; + min-height: 10px; + border-right: 1px solid #AAA; + height: 100%; + font-size: 12px; + line-height: 20px; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: none; + transition: none; + -moz-transition: none; + transition: none; +} + +#animation .animation-bar-right { + height: 20px; + width: 80%; + border-radius: 4px; + background: #DDDBDE; + border: 1px solid #AAA; + overflow: hidden; +} +#animation .animation-bar-right .progress-bar { + float: right; + border-radius: 4px; + min-height: 10px; + border-right: 1px solid #AAA; + height: 100%; + font-size: 12px; + line-height: 20px; + text-align: center; + background-color: #AFC428; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: none; + transition: none; + -moz-transition: none; + transition: none; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} + +h2 { margin-top: 50px;} + +/* ---------------------------------------------------------------------- */ + +@media screen and (max-width: 950px) { + #animation > .animation-content{ + display: block; + } + + #animation .animation-workspace{ + width: 80%; + height: 100%; + margin: auto; + margin-top: 5%; + } + + #animation .animation-content-left{ + position: absolute; + top: 0; + left: -80px; + width: 80px; + background: #CACACA; + } + + #animation .animation-content-right{ + position: absolute; + top: 0; + right: -80px; + width: 80px; + background: #CACACA; + } + + #animation .animation-content-left-toggle-btn{ + display: block; + position: absolute; + top: 0; + left: 80px; + width: 35px; + height: 35px; + } + + #animation .animation-content-right-toggle-btn { + display: block; + position: absolute; + top: 0; + right: 80px; + width: 35px; + height: 35px; + } +} + +@media screen and (max-width: 830px) { + #animation .animation-bar-left { + width: 50%; + margin: 50px 5% 50px auto; + } + + #animation .animation-bar-right { + width: 50%; + margin: 50px 5% 50px auto; + } +} + +/* ---------------------------------------------------------------------- */ + +#animation #template, #animation #module { + display:block +} + +#animation #choice, #animation #validation { + margin: 50px; +} + +#animation #template table tbody tr { + background:white; + color:black; +} + +#animation #template tr:hover { + cursor:pointer; +} + +#animation ul.bar-left, #animation ul.bar-right { + padding-top: 65px; +} + +/* +#animation .gray { + -webkit-filter: grayscale(100%); + -moz-filter: grayscale(100%); + -ms-filter: grayscale(100%); + -o-filter: grayscale(100%); + filter: gray; /* IE 6-9 */ +}*/ + +#animation .animation-solution-cible, #animation .animation-solution-source { + width: 50px; +} + +#animation #animation-cleft li, #animation #animation-cright li { + border-bottom: 2px solid #ccc; + text-align: center; +} + +#animation .animation-addbtn { + font-size: 2.4em; + color: #C6BD20; +} + +#animation .animation-addbtn:hover { + color:#0F66A9; +} + +#animation #nameverif .has-success .form-control { + border-color: #C6BD20; +} + +#animation #nameverif .form-control { + border-radius: 4px; +} +#animation #nameverif .has-success .form-control:focus { + box-shadow:0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 6px #C6BD20; +} + +#animation #nameverif .has-success .form-control-feedback { + color: #C6BD20; +} + +#animation #nameverif .has-normal .form-control { + border-color: #66afe9; +} + +#animation #animation-Connecteur-source, #animation #animation-Module-source { + color: #0D6AAF; +} + +#animation #animation-Connecteur-cible, #animation-Module-cible { + color: #C6BD20; +} + +#animation #choice .btn-primary { + background-color: #444446; + border-color: black; + color: white; +} + +#animation #choice .btn-default { + color: #9D9EA0; +} + +#animation #choice .btn-default, #animation #choice .btn-primary { + padding: 20px; + width: 250px; +} + +#animation .name_solution { + font-variant:small-caps; + letter-spacing:0.5em; + font-size: 1.5em; +} + +#animation #template th { + background: #ECECEC; + height: 20px; + padding: 2px; + text-align: center; +} + +#animation #template .info td { + color: white; + background: #444446; +} + +#animation #template .alert { + width: 400px; +} + +#animation #head { + padding-bottom: 50px; +} + +#animation .selected { + color: #1667A8; +} + +#animation .sub_module_selected:hover { + cursor: pointer; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/css/connector.css b/src/Myddleware/RegleBundle/Resources/public/css/connector.css new file mode 100644 index 000000000..c1596ed84 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/connector.css @@ -0,0 +1,98 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#connexion_connector #soluce_source { + font-size: 1.2em; + margin: 20px 0; + padding: 10px; + width: 280px; + margin-left: 210px; +} + +#connexion_connector #source_test { + margin-left: 235px; +} + +#connexion_connector .help { + margin-top: 5px; +} + +#connexion_connector .picture, #connexion_connector .help { + margin-left: 315px; +} + +#connexion_connector .champs label { + text-align: right; + width: 190px; +} + +#connexion_connector .champs input[type="text"], #connexion_connector #step_modules input[type="text"], +#connexion_connector .champs input[type="password"], #connexion_connector #step_modules input[type="password"] { + margin-left: 20px; + width: 280px; + padding: 4px; +} + +#connexion_connector #step_modules { + margin-left: 17px; +} + +#connexion_connector #step_modules_confirme { + margin-left: 175px; +} + +#connexion_connector #msg_status { + width: 280px; + padding: 10px; + margin-left: 175px; + word-wrap: break-word; +} + +#connexion_connector .picture { + padding-top: 20px; +} + +#connector .ui-state-default, #connector .ui-widget-content .ui-state-default, #connector .ui-widget-header .ui-state-default { + padding-top: 23px; +} + +#connexion_connector .sync { + margin-left: 50px; +} + +#connexion_connector a:hover { + text-decoration:none; +} + +#tabs ul.ui-widget-header li { + max-width: calc(1200px / 1 + 1.8px); + min-width: calc(1200px / 1 + 1.8px); + padding-top: 23px; +} + +#connector #tabs ul.ui-widget-header li { + max-width: calc(1200px / 2 + 1.8px); + min-width: calc(1200px / 2 + 1.8px); + padding-top: 23px; +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/css/flux.css b/src/Myddleware/RegleBundle/Resources/public/css/flux.css new file mode 100644 index 000000000..84075fabe --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/flux.css @@ -0,0 +1,161 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + + +#flux fieldset { + float: left; + height: 160px; + margin: 10px; + width: 270px; + padding-left: 30px; +} + +#flux fieldset.border { + border-right: 1px solid #ccc; +} + +#flux { + padding: 20px 0px; +} + +#flux fieldset label { + display:block; +} + +#flux #form_rule { + font-size: 1.3em; + height: 31px; + width: 715px; +} + +#flux legend { + color: #71CBFF; +} + +#flux legend { + border:none; + color: #444446; +} + +#flux th { + background: #ECECEC; + height: 30px; + padding: 2px; + text-align: center; +} + +#flux tr { + border-bottom: 1px solid #ccc; +} + +#flux .fd_error, #flux .fd_log_e { + background: #F2DEDF; +} + +#flux .fd_cancel, #flux .fd_log_w, #flux .fd_open { + background: #FFECCC; +} + +#flux .gblstatus_open { + color: #845A00; +} + +#flux .gblstatus_error, #flux .log_e { + color: #D60405; +} + +#flux .gblstatus_cancel, #flux .log_w { + color: #F1B753; +} + +#flux .gblstatus_close, #flux .log_s { + color: #9AA932; +} + +#flux .gblstatus_close, #flux .gblstatus_cancel, #flux .gblstatus_error, #flux .gblstatus_open { + font-weight: bold; +} + +#flux tr:hover{ + background: #ECECEC; +} + +#flux table a, #flux table a:hover { + color: #0F66A9; + font-weight: bold; +} + +#flux #headertab { + margin-top:20px; +} + +#flux #headertab td { + height: 50px; + max-width: 200px; + min-width: 140px; + padding: 5px; + word-wrap: break-word; +} + +#flux .zone { + float:left; + padding-top:30px; + padding-left:50px; + width: 380px; + max-width: 380px; + overflow:hidden; + text-align: left; +} + +#flux .zone ul { + list-style:none; +} + +#flux .zone .field { + color: #0F66A9; + display: block !important; +} + +#flux #message tr { + height: 40px; +} + +#flux #message .msg { + width: 300px; + max-width: 300px; + word-wrap:break-word; +} + +#flux .btn-large-mydinv { + line-height: 2em; +} + +#flux .massbtn { + margin-left: 43px; +} + +#flux #massselectall { + float: left; + margin-left: 3px; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/css/home.css b/src/Myddleware/RegleBundle/Resources/public/css/home.css new file mode 100644 index 000000000..067644257 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/home.css @@ -0,0 +1,157 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#panel #info-blocks{ + padding-left: 9%; +} +#panel .onoffswitch { + position: relative; width: 170px; + -webkit-user-select:none; -moz-user-select:none; -ms-user-select: none; + top: 5px; right: 5px; +} +#panel .onoffswitch-checkbox { + display: none; +} +#panel .onoffswitch-label { + display: block; overflow: hidden; cursor: pointer; + border: 2px solid #999999; border-radius: 0px; +} +#panel .onoffswitch-inner { + display: block; width: 200%; margin-left: -100%; + -moz-transition: margin 0.3s ease-in 0s; -webkit-transition: margin 0.3s ease-in 0s; + -o-transition: margin 0.3s ease-in 0s; transition: margin 0.3s ease-in 0s; +} +#panel .onoffswitch-inner:before, .onoffswitch-inner:after { + display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 26px; + font-size: 14px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold; + -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; + border: 2px solid transparent; + background-clip: padding-box; +} +#panel .onoffswitch-inner:before { + padding-left: 10px; + background-color: #0069B4; color: #FFFFFF; +} +#panel .onoffswitch-inner:after { + padding-right: 10px; + background-color: #CCCCCC; color: #333333; + text-align: right; +} +#panel .onoffswitch-switch { + display: block; width: 25px; height: 34px; margin: 0px; + background: #444446; + position: absolute; top: 0; bottom: 0; right: 145px; + -moz-transition: all 0.3s ease-in 0s; -webkit-transition: all 0.3s ease-in 0s; + -o-transition: all 0.3s ease-in 0s; transition: all 0.3s ease-in 0s; +} +#panel .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { + margin-left: 0; +} +#panel .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { + right: 0px; +} + +#panel .info, #panel .infob { + display: inline-block; + float: left; + margin: 20px; + width: 450px; + text-align:center; +} + +#panel .info .list-group { + text-align: left; +} + +#panel .info #periods span { + color: #0F66A9; + font-weight:bold; +} + +#panel .infob { + border: 1px solid #ccc; + min-height: 360px; + border-radius: 4px; +} + +#panel #info { + font-size: 1.2em; + margin: 5px; +} + +#panel #info .value { + color: #0F66A9; + font-weight:bold; +} +#panel #listing-solutions-container { + height: 75px; +} +#panel #listing-solutions-container > div, #panel #listing-solutions-container > button { + display: inline-block; +} +#panel #listing-solutions-container button { + width: 50px; + height: 75px; + vertical-align: top; +} +#panel .scroll-img { + border: 1px solid #ccc; + border-radius: 4px; + width: 697px; + height: 75px; + overflow: hidden; + font-size: 0; +} +#panel .scroll-img ul { + width: 2500px; + height: 600px; + margin: 0; + padding-left: 0; + text-align: left; +} +#panel .scroll-img ul li { + width: 64px; + height: 64px; + display: inline-block; + margin: 5px 0 5px 5px; +} +#panel .scroll-img img{ + filter: grayscale(100%); + -webkit-filter: grayscale(100%); + -moz-filter: grayscale(100%); + -ms-filter: grayscale(100%); + -o-filter: grayscale(100%); +} +#panel .scroll-img img:hover{ + filter: none; + -webkit-filter: none; + -moz-filter: none; + -ms-filter: none; + -o-filter: none; + cursor: pointer; +} +#panel .scroll-img a{ + width: 64px; + height: 64px; +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/css/jcarousel.ajax.css b/src/Myddleware/RegleBundle/Resources/public/css/jcarousel.ajax.css new file mode 100644 index 000000000..eb0cc2087 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/jcarousel.ajax.css @@ -0,0 +1,97 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +.carousel { + width: 65px; + position: relative; + -webkit-user-select:none; + -moz-user-select:none; + -o-user-select:none; + user-select:none; +} + +.carousel > .window { + /*background: #828282; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border:solid 1px #424140;*/ + overflow: hidden; + position: relative; + height: 370px; + /*-webkit-box-shadow: rgba(0,0,0,.6) -1px -2px 2px; + -moz-box-shadow: rgba(0,0,0,.6) 0px 2px 2px;;*/ +} + +.carousel ul { + list-style: none; + margin: 0; + height: 10000px; + top: 0; + left: 0; + position: absolute; +} + +.carousel li { + /*padding: 0 10px 0 10px;*/ + margin: 3px 0 0; + /*opacity: 0.4; */ +} + +.carousel li:hover { + cursor: pointer; + /*opacity: 1;*/ + padding-top: 1px; +} + +.carousel li:first-child { + margin: 0; +} + +.carousel img { + border-radius: 6px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + display: block; +} + +.carousel > a { + cursor: pointer; + display: block; + width: 65px; + height: 29px; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAABBCAYAAAA3408NAAABJElEQVRYw2PQ11RgwIUPBjGsnu/B8QTIXodPHT4DMoH4PwjnWgn9B4rlkGQIUKPBgSCGHzBD9gQy/vc2lPoBlDMgyhCgJh6gAbdgBsDwGh/W/+bacneBaniIMWQRugEw3OvCDfLWUryGABUm4DIAhtMsREAGJWA1BKhAHeiN74QMAYWPh4H0d6AeLRRDgJKcQAOuETIAhld6s/431Za/BtTLiWzIHGINgOEOJx6Qt+aADQHFxg5/poJuZ57lpGI7XdkCUGwx/P//n2I8asioIaOGjBoyasioIaOGjBoyasioIaOGUGQIvg4isZg6HQSqdFWo1mmiSveNah1JJIOWUtSlpVrnGls3fx+km/+L6G4+1QYcqDb0ATWEv9qOXxbI5senDgA17ERBpBXknAAAAABJRU5ErkJggg==); + background-repeat: no-repeat; + background-position: 50% 9px; + text-indent: -9999px; +} + +.carousel > .next { + background-position: 50% -45px; +} + +.carousel > .disabled { + opacity: 0.4; + -moz-opacity: 0.4; + filter:alpha(opacity=40); +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/css/layout.css b/src/Myddleware/RegleBundle/Resources/public/css/layout.css new file mode 100644 index 000000000..8abaac93c --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/layout.css @@ -0,0 +1,271 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#myd_top { + height: 35px; + min-height: 35px; + max-height: 35px; + background: #444446; + border-bottom: 4px solid #C6BD20; + min-width: 1200px; + width: 100%; +} + +#myd_title { + height: 46px; + background: #0F66A9; + margin: 0 auto; + width: 1200px; + color: white; + padding: 14px; + font-weight: bold; +} + +#myd_title a, #myd_title a:hover { + color: #C6BD20; +} + +.myd_loading > img{ + display: none; + width: 43px; + height: 11px; +} + +header { + height: auto; + min-height: 200px; + margin: 0 auto; + width: 1200px; +} + +section { + margin: 0 auto; + width: 1200px; + min-height: 500px; + background: white; +} + +footer { + margin-top: 100px; + padding: 10px; + height: 100px; + min-height: 100px; + max-height: 100px; + background: #444446; + min-width: 1200px; + width: 100% auto; + color: white; + text-align: center; +} + +.clr { + clear: both; +} + +.ctr { + text-align: center; +} + +.left { + float: left; + text-align:left; +} + +.right { + text-align:right; + float: right; +} + + +.task { + width: 150px; + max-width: 150px; + margin: 5px; + display: inline-block; +} + +#fd-title-small span.nb,#fd-title span.nb { + font-size:1.3em; +} + +/* tabs */ + +#tabs { + border: 0px !important; +} + +#tabs ul.ui-widget-header { + background: none; + border: none; +} + +#tabs ul.ui-widget-header li { + height: 80px; + min-width: calc( (1200px / 7) + 1.8px ); + max-width: calc( (1200px / 7) + 1.8px ); + margin: -1px; +} + +#tabs .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: #D7D8DA; + border-left: 2px solid #B7B7B9; + border-right: 2px solid #B7B7B9; + color: #AAAAAC; + opacity: 1; +} + +#tabs .ui-state-active { + background: white; + color: #454547; +} + +#tabs .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { + color: #454547; +} + +#tabs .ui-corner-all, #tabs .ui-corner-top, #tabs .ui-corner-right, #tabs .ui-corner-tr, +#tabs .ui-corner-left, #tabs .ui-corner-tl, +#tabs.ui-corner-br, #tabs .ui-corner-bottom, #tabs .ui-corner-bl { + border-top-right-radius: 0px; + border-top-left-radius: 0px; +} + +#tabs .ui-corner-all, #tabs .ui-corner-bottom, #tabs .ui-corner-right, #tabs .ui-corner-br { + border-bottom-right-radius: 0px; +} +#tabs .ui-corner-all, #tabs .ui-corner-bottom, #tabs .ui-corner-left, #tabs .ui-corner-bl { + border-bottom-left-radius: 0px; +} + +.ui-tabs, .ui-tabs-nav { + padding: 0em !important; +} + +#tabs .ui-tabs .ui-tabs-nav li.ui-tabs-active, #tabs .ui-state-hover, +#tabs .ui-state-active { + border-top: 0px !important; +} + +#tabs .ui-state-disabled { + background: #7D7D7F; + border-top: 9px solid #CCC31C; +} + +#tabs .ui-state-disabled a.ui-tabs-anchor { + color: #343436; +} + +#tabs a.ui-tabs-anchor { + width: 100%; + height: 100%; + text-align: center; +} + +#tabs ul.ui-tabs-nav { + width: 1200px; +} + +#notification { + margin: 0 auto; + width: 1200px; + background:white; + display:none; +} + +#notification #zone_notification { + margin: 0 auto 20px; + text-align: left; + width: 1200px; + max-width: 1200px; + max-height: 120px; + overflow-y:scroll; + padding: 0; + + -moz-box-shadow: 5px 5px 5px 0px #656565; + -webkit-box-shadow: 5px 5px 5px 0px #656565; + -o-box-shadow: 5px 5px 5px 0px #656565; + box-shadow: 5px 5px 5px 0px #656565; + filter:progid:DXImageTransform.Microsoft.Shadow(color=#656565, Direction=134, Strength=5); + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; +} + +#notification #zone_notification li { + list-style-type: none; + padding-left:15px; + min-height: 30px; +} + +#notification #zone_notification li .glyphicon { + float: left; + margin: 0 5px; +} + +#notification #zone_notification .info { + color: #0F66A9; + background: #BFE2FC; +} + +#notification #zone_notification .warning { + color: #f1b753; + background: #ffeccc; +} + +#notification #zone_notification .error { + color: #d60405; + background: #f2dedf; +} + +#notification #zone_notification .success { + color: #9aa932; + background: white; +} + +/* Block de boutons pour les fiches info */ +.block-btn-infos > a { + text-decoration: none; +} + +/* help content */ +.help-content { + border-top: 1px solid #ccc; + margin: 20px 0; + padding-top: 20px; + text-align: justify; + line-height: 2em; +} + +.help-content a { + text-decoration: underline; +} + +.help-content a:hover { + color:#0F66A9; +} + +#blockaddfield { + margin: 10px 0px; + float:right; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/css/menu.css b/src/Myddleware/RegleBundle/Resources/public/css/menu.css new file mode 100644 index 000000000..1999160b7 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/menu.css @@ -0,0 +1,117 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +menu { + margin-top: -5px; +} + +menu ul, menu li { + list-style: none; +} + +menu li { + background: #444446; + display: inline-block; + width: 150px; + padding-top: 5px; + height: 50px; + text-align: center; + font-weight: bold; +} + +menu .dropdown-menu > li > a, menu .dropdown-menu > li > a:hover { + background-color:transparent; + background-image:transparent; + background:transparent; +} + +menu .dropdown-menu .divider { + background-color: #e5e5e5; + height: 2px; + overflow: hidden; + padding: 0; + width: 100%; +} + +menu .dropdown-menu > li > a { + color: #444446; +} + +menu .dropdown-menu > li > a:hover { + color: #0F66A9; +} + +menu li li, #myd_top li li { + background: white; + text-align: left; + width: 200px; + max-width: 200px; + overflow:hidden; + height: 25px; + padding:0px; +} + +menu li li:hover, #myd_top li li:hover { + background: transparent; +} + +#myd_top .nav-pills > li > a:focus, menu .nav-pills > li > a:focus { + background: transparent; +} + +#myd_top li a { + color: white; +} + +#myd_top li li a { + color: #0F66A9; +} + +menu .nav-pills > li > a, menu .nav-pills > li > a:hover, menu .nav .open > a, menu .nav .open > a:hover, menu .nav .open > a:focus { + color: white; + background-color:transparent; + border-radius: none; +} + +menu li:hover { + background: #0F66A9; + cursor:pointer; +} + +#myd_top nav { + padding-right: 150px; +} + +#myd_top .nav-pills > li > a, #myd_top .nav-pills > li > a:hover, #myd_top .nav .open > a, #myd_top .nav .open > a:hover, #myd_top .nav .open > a:focus { + background-color:transparent; + border-radius: none; +} + +#myd_top .nav-pills > li > a:hover, #myd_top .nav .open > a, #myd_top .nav .open > a:hover, #myd_top .nav .open > a:focus { + color: #C6BD20; +} + +#search { + padding-top: 25px; +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/css/regle.css b/src/Myddleware/RegleBundle/Resources/public/css/regle.css new file mode 100644 index 000000000..3e7718601 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/regle.css @@ -0,0 +1,222 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#step_modules { + padding: 20px; +} + +#validation_simulation:hover { + cursor: pointer; +} + +#connexion #rulename { + border: 3px solid transparent; + padding: 5px; + color: white; + background: #202020; +} + +.obl { + display: inline-block; + width:16px; + height:16px; + margin-left: 20px; + padding-left: 5px; +} +.windows { + display:none; +} + + +.clr { + both:clear; +} + +.left { + float: left; + text-align: left; +} + +.right { + float: right; + text-align: right; +} + +.cursor:hover { + cursor:pointer; + color: #12BA00; +} + + + +/* step 3 */ + +.note, #error { + float: left; + margin-right: .3em; + width: 400px; + margin: 20px 0; +} + +.ui-widget-content { + font-size: 1em; +} + +.ui-widget-header { + font-size:0.85em; +} + +/*#cible ul li li::before{ content:"Formule : "; }*/ + + + +#choix fieldset { + border: 0px; +} + +.clr { + clear:both; +} + +.listepager { + min-width:75px; +} + +.listepager td { + padding: 5px; + color: black; + min-width:150px; + +} +.solution_block { + width: 200px; + height: 64px auto; +} + +.listepager .off td { + opacity:0.5; +} + +.listepager tr { + padding: 10px; +} + +.has-switch > div { + height: 30px; +} + +.listepagerflux{ + width: 99%; +} + +.listepagerflux td { + padding: 5px; +} + +/* params */ + + + + +#fields_duplicate_target li { + -moz-user-select: none; + background-image: none; + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 4px; + cursor: pointer; + display: inline-block; + font-size: 14px; + font-weight: 400; + line-height: 1.42857; + margin-bottom: 0; + padding: 6px 12px; + text-align: center; + vertical-align: middle; + white-space: nowrap; + + background-color: #FFFFFF; + border-color: #CCCCCC; + color: #333333; +} + +#fields_duplicate_target li:hover { + cursor: pointer; +} +#fields_duplicate_target li.active { + background-color: #5BC0DE; + border-color: #46B8DA; + color: #FFFFFF; +} + +#fields_duplicate_target li.no_active { + background-color: #D9534F; + border-color: #D43F3A; + color: #FFFFFF; +} + +/* relation step 3 */ + +#relation .rel .obl { + margin-left: 0px; +} + +#relation .rel, #fieldsfilter { + list-style: none outside none; +} + +#fieldsfilter li { + margin: 10px 0px; +} + +#fieldsfilter select { + margin: 0px 10px; +} + +input[type="text"] { + color: black; +} + +/* infobulle mapping des champs */ +.info_delete_fields { + position:absolute; + background: rgba(157,192,225,4); + border: 1px solid #0F66A9; + color: #444446; + min-width: 100px; + padding:0px 4px; + font-weight:bold; + font-size: 0.7em; + margin-left: 50px; +} + +#tabs .badge { + background: #CCC31C; + display:inline-block; + width: 30px; +} + +#champs #catalog { + max-height: 870px; + overflow: auto; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/css/rule.css b/src/Myddleware/RegleBundle/Resources/public/css/rule.css new file mode 100644 index 000000000..634647491 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/rule.css @@ -0,0 +1,502 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#rule .onoffswitch { + position: relative; + width: 74px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + margin: auto; +} +#rule .onoffswitch-checkbox { + display: none; +} +#rule .onoffswitch-label { + display: block; + overflow: hidden; + cursor: pointer; + border: 0px solid #999999; + border-radius: 0px; +} +#rule .onoffswitch-inner { + display: block; + width: 200%; + margin-left: -100%; + -moz-transition: margin 0.3s ease-in 0s; + -webkit-transition: margin 0.3s ease-in 0s; + -o-transition: margin 0.3s ease-in 0s; + transition: margin 0.3s ease-in 0s; +} +#rule .onoffswitch-inner > span { + display: block; + float: left; + position: relative; + width: 50%; + height: 16px; + padding: 0; + line-height: 16px; + font-size: 12px; + color: white; + font-family: Trebuchet, Arial, sans-serif; + font-weight: bold; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +#rule .onoffswitch-inner .onoffswitch-active { + padding-left: 11px; + background-color: #EEEEEE; + color: #FFFFFF; +} +#rule .onoffswitch-inner .onoffswitch-inactive { + padding-right: 11px; + background-color: #EEEEEE; + color: #FFFFFF; + text-align: right; +} +#rule .onoffswitch-switch { + display: block; + width: 31px; + margin: 0px; + text-align: center; + border: 0px solid #999999; + border-radius: 0px; + position: absolute; + top: 0; + bottom: 0; +} +#rule .onoffswitch-active .onoffswitch-switch { + background: #C6BD20; + left: 0; +} +#rule .onoffswitch-inactive .onoffswitch-switch { + background: #555555; + right: 0; +} +#rule .onoffswitch-active .onoffswitch-switch:before { + content: " "; + position: absolute; + top: 0; + left: 31px; + border-style: solid; + border-color: #C6BD20 transparent transparent #C6BD20; + border-width: 8px 7px; +} +#rule .onoffswitch-inactive .onoffswitch-switch:before { + content: " "; + position: absolute; + top: 0; + right: 31px; + border-style: solid; + border-color: transparent #555555 #555555 transparent; + border-width: 8px 7px; +} +#rule .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { + margin-left: 0; +} + +#rule .btn-opt { + color: #555555; + font-size: 1.2em; +} + +#rule .btn-opt:hover { + color: #0F66A9; +} + +#rule th { + background: #ECECEC; + height: 20px; + padding: 2px; + text-align: center; +} + +#rule tr { + border-bottom: 1px solid #ccc; +} + +#rule td { + text-align: center; +} + +#rule td.lft { + text-align: center; +} + +#rule td.lft > a{ + color: black; +} + +#rule .vignette { + width: 50px; +} + + +/* relate */ + +#rule_mapping #relation #relate input.title { + border: none; + min-width: 250px; +} + +/* fields mapping */ + +#source { float:left; width: 250px; margin-right: 2em; overflow:hidden; } + +#source, #cible { + float:left; + padding-top:10px; +} + +#source h1, #cible h1 { padding: .2em; margin: 0; } + +#source li:hover, #cible ol:hover { cursor:move } + +#cible { + width: 815px; + max-width: 815px; + overflow: auto; +} + +#cible .champs { + width: 260px; + float: left; + padding:5px; +} + +#cible ul li { + color:#4297D7; +} + +#cible .ui-widget-header { + background: none repeat scroll 0 0 #ccc31c; + border: 0px; + color: #454539; + font-weight: bold; + text-align: center; + height: 40px; + line-height: 2.5em; + word-wrap: break-word; +} + +#cible .ch, #cible .placeholder { + list-style:none; +} + +#cible .ch { + border-bottom: 1px solid #cccccc; + border-width: 1px 10px 1px 1px; + color: #aaa00b; + line-height: 2em; + margin-right: 5px; +} + +#cible .placeholder { + color: #CCCCCC; + font-weight: bold; +} + +#source .ui-accordion-content { + padding:0px; + border:none; +} + +#source ul { + list-style:none; + padding-left:0px; +} + + +#source .ui-state-active, #source .ui-widget-content .ui-state-active, #source .ui-widget-header .ui-state-active, +#source .ui-state-default, #source .ui-widget-content .ui-state-default, #source .ui-widget-header .ui-state-default { + background:#EDEDED; + border:none; +} +/* +#source .ui-state-active a, #source .ui-state-active a:link, #source .ui-state-active a:visited { + color:#444446; +} */ + +#source h2, #source .h2 { + font-size: 1em; +} + +#rule .ui-widget-content, #cible .ui-widget-content { + border: none; +} + +#cible .ui-widget-content { + height: 150px; + overflow: auto; +} + +#rule #catalog { + height: 680px; + overflow:auto; +} + +#targetfields { + height: 800px; + overflow:auto; + background: #C7C7C7; + clear: both; +} + +#targetfields ol, #targetfields ul { + padding-left: 5px; + list-style: none outside none; +} + +#targetfields p { + padding-bottom: 10px; + padding-left: 25px; +} + +#cible h1.title { + color: #444446; + font-size: 1.3em; +} + +/* formula */ + +#formule .listarea { + min-height: 150px; +} + +#formule #functions { + display: inline-block; + height: 180px; + max-height: 180px; + overflow-y: scroll; +} + +#formule #functions .func { + width: 25px; +} + +#formule #functions .tooltip { + position: relative; + opacity: inherit; + cursor: pointer; + text-decoration:none; +} + +#formule #filter { + height: 30px; +} + +#formule #area_insert, #formule #area_color { + height: 225px; + padding: 10px; + max-height: 225px; + overflow:scroll; +} + +#formule #area_insert { + width: 250px; +} + +#formule #area_color { + min-width: 350px; + width: 350px; +} + +#formule .wdh, #formule #filter, #formule .listarea { + max-width: 300px; + width: 300px; +} + +#formule .formule_text { + list-style-type:none; +} + +/* simulate */ + +#simulation .middle { + width:50%; +} + +#simulation #warning { + width: 400px; +} + +#simulation #validation_simulation { + line-height: 2.5em; + text-align: center; +} + +#simulation #simulation_tab li { + border-bottom: 1px solid #ccc; + min-height: 40px auto; + list-style: none outside none; + padding-top: 10px; +} + +#relation th { + background:none; +} + +#relation td { + padding: 20px; +} + +#relation td.title { + font-weight: bold; + text-align: center; +} + +#relation tr { + border: none; + height: 60px; +} + +#fieldsfilter span { + color: #0F66A9; + font-weight: bold; + font-size: 1.2em; +} + +#fieldsfilter li, #viewfilters li { + height: 40px; + border-bottom: 1px solid #ccc; + list-style:none; +} + +#fieldsfilter select { + border: 0px; +} + +#fieldsfilter input { + float: right; + padding: 5px; + width: 250px; +} + +#rule #tabs ul.ui-widget-header li { + min-width: calc( (1200px / 5) + 1.8px ); + max-width: calc( (1200px / 5) + 1.8px ); +} + +/* filtres */ + +#viewfilters span { + margin: 0px 20px; +} + +#viewfilters .target, #viewfilters .value { + font-weight: bold; + font-size: 1.2em; + +} + +#viewfilters .target { + color: #0F66A9; + display: inline-block; + min-width: 350px; +} + +#viewfilters .value { + color: #0F66A9; + font-size: 1em; +} + +/* correction tabs */ + +#catalog .ui-state-default, #catalog .ui-widget-content .ui-state-default, #catalog .ui-widget-header .ui-state-default { + border-right: 0px; + border-left: 0px; + background: none; + padding: none; +} + +#targetfields .ui-state-default { + border: 0em !important; + background: #E2E2E2; +} + +#cible .ui-state-default .placeholder { + color: #404042; +} + +/* Button saveParam et loading en display inline block */ +#validRuleParams > * { + display: inline-block; +} + +#rule #mapping .fields { + word-wrap: break-word; +} + +#rule #tabs .ui-state-default, #rule #tabs .ui-widget-content .ui-state-default, #rule #tabs .ui-widget-header .ui-state-default, +#rule_mapping #tabs .ui-state-default, #rule_mapping #tabs .ui-widget-content .ui-state-default, #rule_mapping #tabs .ui-widget-header .ui-state-default { + padding-top: 23px; + font-size: 0.8em; +} + +/* Button "help" création de règle */ +.link-help { + position: relative; + top: -100px; + font-size: 1.2em; + margin-top: 25px; + margin-right: 25px; +} + +.link-help-view { + float: right; + font-size: 1.2em; +} + +.link-help > a, .link-help-view > a { + color: #0069b4; +} + +.link-help > a:hover, .link-help-view > a:hover { + color: #cdc400; + text-decoration: none; + cursor: pointer; +} + +.help-content > h2, .help-content > p{ + width: 80%; + margin: auto; +} + +#simulate_list .value { + font-size: 0.9em; + color: #0069b4; +} + +#rule_mapping .rule_arrow_left { + margin-left: 25px; +} + +#rule_mapping .rule_arrow_right { + margin-right: 25px; +} + +#rule_mapping .rule_arrow_left:hover, #rule_mapping .rule_arrow_right:hover { + cursor: pointer; +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/css/task.css b/src/Myddleware/RegleBundle/Resources/public/css/task.css new file mode 100644 index 000000000..127c3445e --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/task.css @@ -0,0 +1,92 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#task { + padding: 20px 0; + text-align: center; +} + +#task th { + background: #ECECEC; + height: 30px; + padding: 2px; + text-align: center; +} + +#task tr { + border-bottom: 1px solid #ccc; +} + +#task .fd_error, #task .fd_open, #task .fd_log_e { + background: #F2DEDF; +} + +#task .fd_cancel, #task .fd_log_w { + background: #FFECCC; +} + +#task .gblstatus_error, #task .gblstatus_open, #task .log_e { + color: #D60405; +} + +#task .gblstatus_cancel, #task .log_w { + color: #F1B753; +} + +#flux .gblstatus_close, #task .log_s { + color: #9AA932; +} + +#task .gblstatus_close, #task .gblstatus_cancel, #task .gblstatus_error, #task .gblstatus_open { + font-weight: bold; +} + +#task tr:hover, #task .fd_normal { + background: #ECECEC; +} + +#task table a, #task table a:hover { + color: #0F66A9; + font-weight: bold; +} + +#task #headertab { + margin-top:20px; +} + +#task #headertab td { + height: 50px; + max-width: 200px; + min-width: 80px; + width: 80px; + padding: 5px; + word-wrap: break-word; +} + +#task .listepager .count { + max-width: 80px; + width: 80px; + min-width: 80px; + text-align: center; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/css/view/connector.css b/src/Myddleware/RegleBundle/Resources/public/css/view/connector.css new file mode 100644 index 000000000..184102791 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/view/connector.css @@ -0,0 +1,66 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#connector #tabs li { + width: 50.16%; +} + +#connector #tab_connector th { + background: none repeat scroll 0 0 #ececec; + height: 20px; + padding: 2px; + text-align: center; +} + + +#connector #tab_connector { + width: 600px; + margin: 0px 0px 20px 0px; +} + + +#connector #tab_connector td { + +} + +#connector #tab_connector td.title { + width: 35%; + color:#0F66A9; + text-align:right; + padding-right: 10px; +} + + +#connector #tab_connector td.solution { + height: 80px; +} + +#connector #tab_connector td.value { + width: 65%; +} + +#connector #tab_connector td.value input { + width:100%; + padding:8px; +} \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/css/view/rule.css b/src/Myddleware/RegleBundle/Resources/public/css/view/rule.css new file mode 100644 index 000000000..4bd151de2 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/css/view/rule.css @@ -0,0 +1,199 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +#rule td.value { + height: 40px; +} + +#rule .tabinfo { + margin: 50px; +} + +#rule .tabinfo .title_source, #rule .tabinfo .title_source_ctr { + color: #0F66A9; +} + +#rule .tabinfo .title_target, #rule .tabinfo .title_target_ctr { + color: #C6BD20; +} + +#rule #tab_rule { + width: 400px; +} + +#rule #tab_connector { + width: 500px; +} + +#rule .title, #rule .title_target, #rule .title_source, #rule .title_target_ctr, #rule .title_source_ctr { + text-align: left; +} + +#rule .glyphicon-edit:hover { + color: #0F66A9; +} + +#rule .title_target_ctr, #rule .title_source_ctr { + font-weight: bold; + font-size:1.2em; +} + +#rule .onoffswitch { + margin-left: 55px; +} + +#rule .mapping { + float: left; + margin: 20px; + width: 300px; + + padding:5px; +} + +#rule .mapping .title { + color: #454547; + cursor: pointer; + display: block; + text-align: center; + height: 40px; + line-height: 2.5em; + margin-left: 8px; + margin-top: 5px; +} + +#rule .mapping .title { + background:#CCC31C; + font-weight: bold; +} + +#rule .mapping .fields { + background:white; +} + +#rule .mapping .formula { + color: #0E68A7; +} + +#rule .gray { + box-shadow:2px 2px 2px grey, + -1px -1px 2px white; +} + +#rule .fields .field { + color: #CCC31C; +} + +#rule .fields div { + padding-left: 10px; +} + +#rule #relate th { + background:none; +} + +#rule #relate td { + padding: 20px; +} + +#rule #relate td.title { + font-weight: bold; + text-align: center; +} + +#rule #relate tr { + border: none; + height: 60px; +} + +#rule #relate a:hover { + text-decoration: none; + color: #0E68A7; +} + +#ruleparams { + background: #444446; + color: white; + margin-bottom: 20px; + padding: 10px; + width: 500px; +} + +#ruleparams select, input { + background: white; + color: #444446; + margin: 2px; + padding: 5px; + border:none; + width: 180px; +} + +#rule #tabs #mapping #fd-title { + margin: 20px 20px 20px -35px; +} + +#rule a { + color: #428bca; +} + +#ruleparams p { + margin: 0 auto; + max-width: 400px; +} + +#ruleparams input, #ruleparams select { + display:block; +} + +/* Simulation Flux fiche règle */ +#simuleRuleFluxContainer { + background: #444446; + color: white; + margin-top: 20px; + margin-bottom: 20px; + padding: 10px; + width: 500px; + min-height: 100px; +} + +.simuleRuleFluxResult { + display: inline-block; + margin: 5px; +} + +#simuleRuleFluxResult { + font-weight: bold; +} + +#simuleRuleFluxDetails { + font-size: 0.7em; + text-align: left; +} + +#simuleRuleFluxBtn > * { + display: inline-block; +} + +#simuleRuleFluxError, #simuleRuleFluxLoading_img { + display: none; +} diff --git a/src/Myddleware/RegleBundle/Resources/public/images/add.png b/src/Myddleware/RegleBundle/Resources/public/images/add.png new file mode 100644 index 000000000..ad63c965d Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/add.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/croix.png b/src/Myddleware/RegleBundle/Resources/public/images/croix.png new file mode 100644 index 000000000..588c23d53 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/croix.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/data.png b/src/Myddleware/RegleBundle/Resources/public/images/data.png new file mode 100644 index 000000000..7003f7883 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/data.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/filter.png b/src/Myddleware/RegleBundle/Resources/public/images/filter.png new file mode 100644 index 000000000..bcf0fbbbb Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/filter.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/help.png b/src/Myddleware/RegleBundle/Resources/public/images/help.png new file mode 100644 index 000000000..4fb64bb7a Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/help.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/loader-cible.gif b/src/Myddleware/RegleBundle/Resources/public/images/loader-cible.gif new file mode 100644 index 000000000..3ce45c140 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/loader-cible.gif differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/loader-source.gif b/src/Myddleware/RegleBundle/Resources/public/images/loader-source.gif new file mode 100644 index 000000000..93ffc8451 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/loader-source.gif differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/loader.gif b/src/Myddleware/RegleBundle/Resources/public/images/loader.gif new file mode 100644 index 000000000..9a6b4f04b Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/loader.gif differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/loading.gif b/src/Myddleware/RegleBundle/Resources/public/images/loading.gif new file mode 100644 index 000000000..0ca7ada96 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/loading.gif differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/locale-loader.gif b/src/Myddleware/RegleBundle/Resources/public/images/locale-loader.gif new file mode 100644 index 000000000..e66533578 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/locale-loader.gif differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/logo/down.png b/src/Myddleware/RegleBundle/Resources/public/images/logo/down.png new file mode 100644 index 000000000..fb7c5e67a Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/logo/down.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/logo/logo.png b/src/Myddleware/RegleBundle/Resources/public/images/logo/logo.png new file mode 100644 index 000000000..18a90814a Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/logo/logo.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/logo/text.png b/src/Myddleware/RegleBundle/Resources/public/images/logo/text.png new file mode 100644 index 000000000..c80d48ea1 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/logo/text.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/logo/up.png b/src/Myddleware/RegleBundle/Resources/public/images/logo/up.png new file mode 100644 index 000000000..afc38587f Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/logo/up.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/need.png b/src/Myddleware/RegleBundle/Resources/public/images/need.png new file mode 100644 index 000000000..c5d06a596 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/need.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/bittle.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/bittle.png new file mode 100644 index 000000000..fdbdfdd4e Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/bittle.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/database.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/database.png new file mode 100644 index 000000000..b0db01ae3 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/database.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/dolist.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/dolist.png new file mode 100644 index 000000000..254588856 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/dolist.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/drupal.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/drupal.png new file mode 100644 index 000000000..42f85c0f6 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/drupal.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/eventbrite.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/eventbrite.png new file mode 100644 index 000000000..c9688f4eb Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/eventbrite.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/exacttarget.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/exacttarget.png new file mode 100644 index 000000000..70cd7ee74 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/exacttarget.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/file.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/file.png new file mode 100644 index 000000000..cc5ba92b1 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/file.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/googlecontacts.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/googlecontacts.png new file mode 100644 index 000000000..bb042c564 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/googlecontacts.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/googlemail.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/googlemail.png new file mode 100644 index 000000000..30c1be7cc Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/googlemail.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/googleplus.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/googleplus.png new file mode 100644 index 000000000..69d2cc0aa Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/googleplus.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/gotowebinar.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/gotowebinar.png new file mode 100644 index 000000000..6e0caafef Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/gotowebinar.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/joomla.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/joomla.png new file mode 100644 index 000000000..7f7fcc4b0 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/joomla.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/linkedin.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/linkedin.png new file mode 100644 index 000000000..a58077131 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/linkedin.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/magento.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/magento.png new file mode 100644 index 000000000..602308cd3 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/magento.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/mailchimp.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/mailchimp.png new file mode 100644 index 000000000..5564111fe Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/mailchimp.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/mailjet.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/mailjet.png new file mode 100644 index 000000000..3ad7ec6b0 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/mailjet.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/mantis.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/mantis.png new file mode 100644 index 000000000..10ac05b71 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/mantis.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/moodle.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/moodle.png new file mode 100644 index 000000000..eeb07de9c Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/moodle.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/prestashop.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/prestashop.png new file mode 100644 index 000000000..e4c1b8dad Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/prestashop.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/sagecrm.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/sagecrm.png new file mode 100644 index 000000000..cf9d5ccfb Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/sagecrm.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/salesforce.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/salesforce.png new file mode 100644 index 000000000..60eef9171 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/salesforce.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/sap.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/sap.png new file mode 100644 index 000000000..066cd0fec Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/sap.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/sapcrm.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/sapcrm.png new file mode 100644 index 000000000..127c63320 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/sapcrm.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/shopify.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/shopify.png new file mode 100644 index 000000000..58e27afe7 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/shopify.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/sugarcrm.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/sugarcrm.png new file mode 100644 index 000000000..f454aa734 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/sugarcrm.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/suitecrm.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/suitecrm.png new file mode 100644 index 000000000..dd857e427 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/suitecrm.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/twitter.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/twitter.png new file mode 100644 index 000000000..464f25db2 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/twitter.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/woocommerce.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/woocommerce.png new file mode 100644 index 000000000..b226a83ea Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/woocommerce.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/solution/wordpress.png b/src/Myddleware/RegleBundle/Resources/public/images/solution/wordpress.png new file mode 100644 index 000000000..8f237a504 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/solution/wordpress.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/status_offline.png b/src/Myddleware/RegleBundle/Resources/public/images/status_offline.png new file mode 100644 index 000000000..e500c4722 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/status_offline.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/status_online.png b/src/Myddleware/RegleBundle/Resources/public/images/status_online.png new file mode 100644 index 000000000..d9a9a9816 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/status_online.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/arrow-mapping.png b/src/Myddleware/RegleBundle/Resources/public/images/template/arrow-mapping.png new file mode 100644 index 000000000..fd5811ae4 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/arrow-mapping.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/arrow-menu.png b/src/Myddleware/RegleBundle/Resources/public/images/template/arrow-menu.png new file mode 100644 index 000000000..b6ea6daa0 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/arrow-menu.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/btn-large.png b/src/Myddleware/RegleBundle/Resources/public/images/template/btn-large.png new file mode 100644 index 000000000..3824fca3c Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/btn-large.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/btn-middle.png b/src/Myddleware/RegleBundle/Resources/public/images/template/btn-middle.png new file mode 100644 index 000000000..c2d70099e Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/btn-middle.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/btn-small.png b/src/Myddleware/RegleBundle/Resources/public/images/template/btn-small.png new file mode 100644 index 000000000..05ee63300 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/btn-small.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/btn.png b/src/Myddleware/RegleBundle/Resources/public/images/template/btn.png new file mode 100644 index 000000000..e74f2f1e9 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/btn.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/cloud.png b/src/Myddleware/RegleBundle/Resources/public/images/template/cloud.png new file mode 100644 index 000000000..4172f18e9 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/cloud.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title-large.png b/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title-large.png new file mode 100644 index 000000000..251ff6809 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title-large.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title-small.png b/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title-small.png new file mode 100644 index 000000000..b84bed1ae Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title-small.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title.png b/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title.png new file mode 100644 index 000000000..204100e3d Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/fd-title.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/fd.jpg b/src/Myddleware/RegleBundle/Resources/public/images/template/fd.jpg new file mode 100644 index 000000000..095f5b043 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/fd.jpg differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/history.png b/src/Myddleware/RegleBundle/Resources/public/images/template/history.png new file mode 100644 index 000000000..28b3bea07 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/history.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/left-arc-color.png b/src/Myddleware/RegleBundle/Resources/public/images/template/left-arc-color.png new file mode 100644 index 000000000..b85b27218 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/left-arc-color.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/left-arc-gray.png b/src/Myddleware/RegleBundle/Resources/public/images/template/left-arc-gray.png new file mode 100644 index 000000000..395e52c73 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/left-arc-gray.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/logo.png b/src/Myddleware/RegleBundle/Resources/public/images/template/logo.png new file mode 100644 index 000000000..e326ba551 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/logo.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/right-arc-color.png b/src/Myddleware/RegleBundle/Resources/public/images/template/right-arc-color.png new file mode 100644 index 000000000..3f5d76a62 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/right-arc-color.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/right-arc-gray.png b/src/Myddleware/RegleBundle/Resources/public/images/template/right-arc-gray.png new file mode 100644 index 000000000..99d61e2f2 Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/right-arc-gray.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/source.png b/src/Myddleware/RegleBundle/Resources/public/images/template/source.png new file mode 100644 index 000000000..2820412ea Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/source.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/images/template/target.png b/src/Myddleware/RegleBundle/Resources/public/images/template/target.png new file mode 100644 index 000000000..60f4f97ec Binary files /dev/null and b/src/Myddleware/RegleBundle/Resources/public/images/template/target.png differ diff --git a/src/Myddleware/RegleBundle/Resources/public/js/account.js b/src/Myddleware/RegleBundle/Resources/public/js/account.js new file mode 100644 index 000000000..205e3503e --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/account.js @@ -0,0 +1,105 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +$( document ).ready(function() { + + positionningPopup('#myd_change_locale','#user_account'); + + /****** + * BOUTONS CHANGEMENT DE LANGUE + */ + + $('#btn-locale-en','#user_account').click(function() { + if(!$("#btn-locale-en").is(".disabled")) { + $.ajax({ + type: "POST", + url: path_locale, + data:{ + locale : "en" + }, + beforeSend: function(){ + $('#myd_change_locale','#user_account').fadeIn(); + }, + success: function(){ + document.location.reload(true) + } + }); + } + }); + + $('#btn-locale-fr','#user_account').click(function() { + if(!$("#btn-locale-fr").is(".disabled")) { + $.ajax({ + type: "POST", + url: path_locale, + data:{ + locale : "fr" + }, + beforeSend: function(){ + $('#myd_change_locale','#user_account').fadeIn(); + }, + success: function(){ + document.location.reload(true) + } + }); + } + }); +}); + +// Positionne les différentes Popup au centre de la page en prenant en compte le scroll +function positionningPopup(id, idcontainer) { + // Positionnement du bloc transparent (qui permet d'éviter les clics le temps de la popup) + $(id,idcontainer).css({ + "height": $(document).height(), + "width": $(document).width(), + }); + + // Positionnement initial (au centre de l'écran) + scroll + $(id + "> .user_account_popup_content",idcontainer).css({ + "top": $(window).height()/2 - 200 + $(window).scrollTop(), + "left": $(window).width()/2 - 150, + }); + + // On déclenche un événement scroll pour mettre à jour le positionnement au chargement de la page + $(window).trigger('scroll'); + + $(window).scroll(function(event){ + $(id + "> .user_account_popup_content",idcontainer).css({ + "top": $(window).height()/2 - 200 + $(window).scrollTop() + }); + }); + + // Repositionnement si resize de la fenêtre + $(window).resize(function(){ + $(id,idcontainer).css({ + "height": $(document).height(), + "width": $(document).width(), + }); + + $(id + "> .user_account_popup_content",idcontainer).css({ + "top": $(window).height()/2 - 200 + $(window).scrollTop(), + "left": $(window).width()/2 - 150, + }); + }); +} diff --git a/src/Myddleware/RegleBundle/Resources/public/js/animation.js b/src/Myddleware/RegleBundle/Resources/public/js/animation.js new file mode 100644 index 000000000..b6dc7269d --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/animation.js @@ -0,0 +1,818 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +$(document).ready(function() { + + // Initialisation des progressBar (à leurs positions initiales) + progressBar(0, $('#animation-Bar1'), 0); + progressBar(0, $('#animation-Bar2'), 0); + progressBar(0, $('#animation-Bar3'), 0); + progressBar(0, $('#animation-Bar4'), 0); + + // Positionne le logo Myddleware + posMyddleware(); + + // Resize de window = Repositionnement du Logo + $(window).resize(function() { + posMyddleware(); + }); + + // Clic sur une solution source -> toSource (affichage de la solution sur le cylindre source) + $("img[class^='animation-solution-source']").on('click',function(e){ + toSource(e,$(this)); + }); + + // Clic sur une solution cible -> toCible (affichage de la solution sur le cylindre cible) + $("img[class^='animation-solution-cible']").on('click',function(e){ + toCible(e,$(this)); + }); + + // Clic sur les boutons d'affichage = toggle des 2 carrousels + $(".animation-content-left-toggle-btn").one('click', content_left_toggle); + $(".animation-content-right-toggle-btn").one('click', content_right_toggle); + + $('#btn_validation').click(function(){ + animValidation(); + }); + + // Fermeture de la fancybox + $(".fancybox_animation").fancybox({ + 'beforeClose': function() { + addSolution(); + }, + 'closeClick': true + }); + + // Cache les éléments au chargement de la page + hideLoad(); + + // Affiche la liste des modules si le connecteur est sélectionné + addModule('source'); + addModule('cible'); + + // Contrôle le nom de la règle + $( '#rulename' ).keyup(function() { + controleRuleName($(this).val()); + }); + + // Traitement bouton choix + $('button','#choice').click(function(){ + + choice = $(this).attr('id'); + + if(choice == 'btn_module') { + $('#validation').hide(); + $(this).attr('class','btn btn-primary'); + $('#btn_template').attr('class','btn btn-default'); + + $('#module').fadeIn(1000); + $('#template').fadeOut(1000); + $('#btn_validation').html(trans_btn_mapping); + } + else if(choice == 'btn_template') { + + $(this).attr('class','btn btn-primary'); + $('#btn_module').attr('class','btn btn-default'); + + $('#template').fadeIn(1000); + $('#module').fadeOut(1000); + $('#submodules').fadeOut(1000); + $('#btn_validation').html(trans_btn_confirm); + } + }); + + //gray(); + + // sub modules ex SAP -------------------- + $('#animation-Module-source').change(function() { + getSubModules('source'); + }); + + $('#animation-Module-cible').change(function() { + getSubModules('cible'); + }); + // sub modules ex SAP -------------------- + +}); + + +// Récupère les sousmodules de la solution +function getSubModules(type) { + + op_type = ((type == 'source') ? 'cible' : 'source'); + + $.ajax({ + type: "POST", + data:{ + type : type, + connector : $('#animation-Connecteur-' + type ).val(), + module : $('#animation-Module-' + type ).val(), + }, + url: path_submodules, + success: function(data){ + if(data == 0) { + $('#animation-Module-' + type ).attr('data-block',0); + if($('#animation-Module-' + type ).attr('data-block') == 0 && $('#animation-Module-' + op_type ).attr('data-block') == 0 ) { + $('#submodules').hide(); + $('#validation').fadeIn(2000); + } + } + else { + $('#animation-Module-' + type ).attr('data-block',1); + $('#validation').hide(); + $('#submodules').fadeIn(1000); + $('#submodules').html(data); + } + + $('.sub_module_selected').on('click',function() { + + var field_selected = $(this); + $(this).addClass('selected'); + + id = $(this).find('span').attr('data-id'); + + if (typeof id === "undefined") { + id = $(this).find('span').next().attr('data-id'); + } + + $.ajax({ + type: "POST", + data:{ + type : type, + ids : id, + connector : $('#animation-Connecteur-' + type ).val(), + module : $('#animation-Module-' + type ).val(), + select : 1 + }, + url: path_submodules, + success: function(data){ + if( data == 1 ) { + $('#validation').fadeIn(2000); + } + else if( data == 2 ) { + field_selected.removeClass('selected'); + } + } + }); + + }); + } + }); +} + + +// Affiche ou désactive l'arc de cercle +function arc(type, style) { +if(style) { + if(type == 'source') { + s_source = $('#animation-source-container').parent(); + s_source.fadeOut(); + s_source.attr('class','animation-puce arcleft-c'); + s_source.fadeIn(3500); + } + else { + s_cible = $('#animation-cible-container').parent(); + s_cible.fadeOut(); + s_cible.attr('class','animation-puce arcright-c'); + s_cible.fadeIn(3500); + } +} +else { + if(type == 'source') { + s_source = $('#animation-source-container').parent(); + s_source.fadeOut(); + s_source.attr('class','animation-puce arcleft-g'); + s_source.fadeIn(3500); + } + else { + s_cible = $('#animation-cible-container').parent(); + s_cible.fadeOut(); + s_cible.attr('class','animation-puce arcright-g'); + s_cible.fadeIn(3500); + } +} + + +} + + +// Actualise l'écoute des solutions +function listenSolution() { + // Clic sur une solution source -> toSource (affichage de la solution sur le cylindre source) + $("img[class^='animation-solution-source']").on('click',function(e){ + toSource(e,$(this)); + }); + + // Clic sur une solution cible -> toCible (affichage de la solution sur le cylindre cible) + $("img[class^='animation-solution-cible']").on('click',function(e){ + toCible(e,$(this)); + }); +} + +// Permet le grayscale sur les images +function gray() { + + // Fade in images so there isn't a color "pop" document load and then on window load + $(".gray").fadeIn(500); + + // clone image + $('.gray').each(function(){ + + $(this).removeClass('gray'); + classe = $(this).attr('class'); + + var el = $(this); + el.css({"position":"absolute"}).wrap("
").clone().addClass(classe+' grayout').css({"position":"absolute","z-index":"998","opacity":"0"}).insertBefore(el).queue(function(){ + var el = $(this); + el.parent().css({"width":this.width,"height":this.height}); + el.dequeue(); + }); + + + this.src = grayscale(this.src); + + }); + + listenSolution(); + + // Fade image + $('.grayout').mouseout(function(){ + //$(this).parent().find('img:first').stop().animate({opacity:1}, 1000); + $(this).stop().animate({opacity:0}, 1000); + }) + $('.grayout').mouseover(function(){ + $(this).parent().find('img:first').stop().animate({opacity:1}, 1000); + }); + +} + +// Listes non visibles au début +function hideLoad() { + + $('#choice').hide(); + $('#module').hide(); + $('#template').hide(); + $('#submodules').hide(); + $('#validation').hide(); + + $('#animation-Connecteur-source').hide(); + $('#animation-Connecteur-cible').hide(); + //-- + $('#animation-Module-source').hide(); + $('#animation-Module-cible').hide(); + //-- + $('#connector-source-error').hide(); + $('#connector-cible-error').hide(); + //-- + $('#connector-source-success').hide(); + $('#connector-cible-success').hide(); + //-- + $('.loader-source').hide(); + $('.loader-cible').hide(); + //-- +} + +// Liste des templates +function addTemplate() { + $.ajax({ + type: "POST", + url: path_template, + success: function(data){ + $('#template').html(data); + selectTemplate(); + } + }); +} + +function selectTemplate() { + $('#template tbody tr').on('click',function(){ + $('#template tr').removeAttr('class'); + $(this).addClass('info'); + $('#validation').fadeIn(1000); + }); +} + +// Affiche les choix quand source et cible sont OK +function addChoice() { + + source = $('img','#animation-source').attr('data-send'); + cible = $('img','#animation-cible').attr('data-send'); + + // si la source et cible sont OK + if(source == 'success' && typeof source !== "undefined" + && cible == 'success' && typeof cible !== "undefined" ) { + + addTemplate(); + + $('#btn_module').attr('class','btn btn-default'); + $('#btn_template').attr('class','btn btn-default'); + + $('#choice').fadeIn(2000); + } + else { + hideElements(); + } + +} + +// Affiche la liste des modules si le connecteur est sélectionné +function addModule(type) { + $('#animation-Connecteur-' + type).change(function() { + + $('#connector-' + type +'-error').hide(); + $('#connector-' + type +'-success').hide(); + $('#animation-Module-' + type).hide(); + $('.loader-' + type).hide(); + + value = $(this).val(); + if(value != '') { + + // Récupère la liste des modules + $.ajax({ + type: "POST", + url: path_module, + data:{ + type : type, + id : value, + }, + beforeSend: function() { + $('.loader-' + type).show(); + $('#animation-Connecteur-'+ type).attr('disabled','disabled'); + }, + success: function(data){ + $('#animation-Connecteur-'+ type).removeAttr('disabled'); + r = data.split(';'); + imgCount = $('img','#animation-'+type).attr('data-count'); + + if ( r.length > 1 ) { // error + $('#connector'+type+'-success').hide(); + id = '#connector-'+type+'-error'; + $(id).html(' '+r[0]).fadeIn(1000); + + if( typeof imgCount !== "undefined" ) { + + if(imgCount>0) { + $('img','#animation-'+type).removeAttr('data-count'); + ((type == 'source') ? source_toggle() : cible_toggle() ); + } + } + + $('img','#animation-'+type).removeAttr('data-send'); + addChoice(); + + setTimeout(function(){ + arc(type, false); + }, 4000); + } + else { // success + arc(type, true); + $('#animation-Module-' + type).empty(); + $('#connector'+type+'-error').hide(); + id = '#connector-'+type+'-success'; + $(id).html(' Success').fadeIn(1000); + $('#animation-Module-' + type).append(data); + $('#animation-Module-' + type).fadeIn(1000); + + if( typeof imgCount !== "undefined" ) { + imgCount++; + $('img','#animation-'+type).attr('data-count',imgCount) + } + else { + imgCount = 1; + $('img','#animation-'+type).attr('data-count',imgCount) + } + if(imgCount==1) { + + ((type == 'source') ? source_toggle() : cible_toggle() ); + } + + $('img','#animation-'+type).attr('data-send','success'); + addChoice(); + } + + $('.loader-' + type).hide(); + } + }); + } + else { + $('img','#animation-'+type).removeAttr('data-count'); + $('img','#animation-'+type).removeAttr('data-send'); + ((type == 'source') ? source_toggle() : cible_toggle() ); + $('#animation-Module-' + type).hide(); + } + }); +} + +// Ajoute les solutions manquantes +function addSolution() { + $.ajax({ + type: "POST", + url: path_new_solution, + success: function(data){ + if(data != 0) { + values = data.split(';'); + // 0 : source 1 : solution : 2 multi 3 : id + if ( typeof values[0] !== "undefined" && typeof values[1] !== "undefined" && typeof values[2] !== "undefined") { + + if(values[0] == 'source') { + plus = $('.clr','#animation-cleft').find('.fancybox_animation').parent(); + } + else { + plus = $('.clr','#animation-cright').find('.fancybox_animation').parent(); + } + + content = '
  • '+ values[1] +'
  • '; + $(content).insertAfter(plus).fadeIn(1000); + + if(values[2] == 2) { + if(values[0] == 'source') { + plus = $('.clr','#animation-cright').find('.fancybox_animation').parent(); + content = '
  • '+ values[1] +'
  • '; + $(content).insertAfter(plus).fadeIn(1000); + } + else { + plus = $('.clr','#animation-cleft').find('.fancybox_animation').parent(); + content = '
  • '+ values[1] +'
  • '; + $(content).insertAfter(plus).fadeIn(1000); + } + } + } + + listenSolution(); + } // end success + } + }); +} + +// Ajoute la liste des connecteurs en fonction de la solution choisi +function addConnector(type,solution) { + + pos = ((type == 'source') ? 'left' : 'right' ); + + $('li','#animation-c'+pos).find('img').each(function(){ + if($.trim($(this).attr('data-solution')) == $.trim(solution)) { + id = $(this).attr('data-id'); + } + }); + + $.ajax({ + type: "POST", + url: path_connector_by_solution, + data:{ + id : id, + }, + success: function(data){ + $('#animation-Connecteur-' + type).empty(); + $('#animation-Connecteur-' + type).append(data); + $('#animation-Connecteur-' + type).fadeIn(1000); + } + }); + + $('.loader-' + type).hide(); + $('#animation-Connecteur-'+ type).removeAttr('disabled'); + $('#btn_module').attr('class','btn btn-default'); + $('#btn_template').attr('class','btn btn-default'); + hideElements(); +} + +function hideElements() { + $('#choice').fadeOut(2000); + $('#module').fadeOut(1000); + $('#template').fadeOut(1000); + $('#submodules').fadeOut(1000); + $('#validation').fadeOut(1000); +} + + +// Validation générale +function animValidation() { + + rulename = $('#nameverif').find('div').attr('class'); + + if(rulename == 'form-group has-success has-feedback') { + if( $('#btn_module').attr('class') == 'btn btn-primary') { + if( $('#animation-Module-source').val() != '' && $('#animation-Module-cible').val() != '' ) { + animConfirm('module'); + } + } + else if( $('#btn_template').attr('class') == 'btn btn-primary' ) { + animConfirm('template'); + } + } else { + $('div','#nameverif').attr('class','form-group has-error has-feedback'); + $('span','#nameverif').attr('class','glyphicon glyphicon-remove form-control-feedback'); + $('#rulename').focus(); + } + +} + +function animConfirm(choice_lst) { + + template = ''; + if(choice_lst == 'template') { + $('#template').find('tr').each(function(){ + if($(this).attr('class') == 'info') { + template = $(this).find('span').attr('data-id'); + } + }); + } + + $.ajax({ + type: "POST", + data:{ + choice_select : choice_lst, + name : $('#rulename').val(), + module_source : $('#animation-Module-source').val(), + module_target : $('#animation-Module-cible').val(), + template : template, + }, + url: path_validation, + beforeSend: function() { + $('#btn_validation').empty(); + $('#btn_validation').html(wait); + $('#btn_validation').removeAttr('id'); + }, + success: function(data){ + if(data == 'module') { + $(location).attr('href',path_mapping); + } + else if(data == 'template') { + $(location).attr('href',path_listrule); + } + } + }); +} + +// Contrôle le nom de la règle +function controleRuleName(nameValue) { + + if(nameValue.length > 2 ) { + $.ajax({ + type: "POST", + url: path_rulename, + data:{ + name : nameValue + }, + success: function(data){ + + // true + if( data == 0 ) { + $('div','#nameverif').attr('class','form-group has-success has-feedback'); + $('span','#nameverif').attr('class','glyphicon glyphicon-ok form-control-feedback'); + + } + else { // false + $('div','#nameverif').attr('class','form-group has-error has-feedback'); + $('span','#nameverif').attr('class','glyphicon glyphicon-remove form-control-feedback'); + } + } + }); + } + else { + $('div','#nameverif').attr('class','form-group has-normal has-feedback'); + $('span','#nameverif').attr('class','glyphicon form-control-feedback'); + } +} + +// Transfert l'image de la solution choisie au cylindre de la solution source +function toSource(e,solution) { + + var src = solution.attr("src"); + var alt = solution.attr("alt"); + + // si la même solution existe deja alors on quitte + if( $('img','#animation-source').attr('alt') == alt ) { + return false; + } + + $('img','#animation-source').removeAttr('data-count'); + + if(source){ + if(logo) + animateLogo(); + $("#animation-myddleware-back-2 > .animation-myddleware-logo").animate({ width: 0 }, 2000); + setTimeout(function(){ + progressBar(0, $('#animation-Bar1'), 2000); + progressBar(0, $('#animation-Bar2'), 2000); + setTimeout(function(){source = false}, 2005); + }, 2000); + } + + + if(!(src == $("#animation-source > img").attr("src"))){ + e.stopPropagation(); + $("#animation-source-container").fadeOut(1000); + setTimeout(function(){$("#animation-source > img").attr("src", src)}, 1000); + setTimeout(function(){$("#animation-source > img").attr("alt", alt)}, 1000); + $("#animation-source-container").fadeIn(1000); + + addConnector('source',alt); + // gestion des erreurs + $('#connector-source-error').hide(); + $('#connector-source-success').hide(); + // gestion des erreurs + $('#animation-Module-source').hide(); + $('#animation-Module-source').empty(); + } + +} + +// Transfert l'image de la solution choisie au cylindre de la solution cible +function toCible(e,solution) { + + var src = solution.attr("src"); + var alt = solution.attr("alt"); + + // si la même solution existe deja alors on quitte + if( $('img','#animation-cible').attr('alt') == alt ) { + return false; + } + + $('img','#animation-cible').removeAttr('data-count'); + + if(cible){ + if(logo) + animateLogo(); + $("#animation-myddleware-back-3 > .animation-myddleware-logo").animate({ width: 0 }, 2000); + setTimeout(function(){ + progressBar(0, $('#animation-Bar3'), 2000); + progressBar(0, $('#animation-Bar4'), 2000); + setTimeout(function(){cible = false}, 2005); + }, 2000); + } + + if(!(src == $("#animation-cible > img").attr("src"))){ + e.stopPropagation(); + $("#animation-cible-container").fadeOut(1000); + setTimeout(function(){$("#animation-cible > img").attr("src", src)}, 1000); + setTimeout(function(){$("#animation-cible > img").attr("alt", alt)}, 1000); + $("#animation-cible-container").fadeIn(1000); + + addConnector('cible',alt); + // gestion des erreurs + $('#connector-cible-error').hide(); + $('#connector-cible-success').hide(); + // gestion des erreurs + $('#animation-Module-cible').hide(); + $('#animation-Module-cible').empty(); + } +} + +// Anime la connexion de la source à Myddleware +var source = false; +function source_toggle() { + + if(source){ + // if(cible) { + // animateLogo(); + // } + $("#animation-myddleware-back-2 > .animation-myddleware-logo").animate({ width: 0 }, 2000); + setTimeout(function(){ + progressBar(0, $('#animation-Bar1'), 2000); + progressBar(0, $('#animation-Bar2'), 2000); + setTimeout(function(){source = false}, 2005); + }, 2000); + + setTimeout(function(){ + arc('source', false); + }, 4000); + } else { + progressBar(100, $('#animation-Bar1'), 2000); + progressBar(100, $('#animation-Bar2'), 2000); + setTimeout(function(){ + source = true; + $("#animation-myddleware-back-2 > .animation-myddleware-logo").animate({ width: 105 }, 2000) + }, 2005); + } +} + +// Anime la connexion de la cible à Myddleware +var cible = false; +function cible_toggle() { + + if(cible){ + $("#animation-myddleware-back-3 > .animation-myddleware-logo").animate({ width: 0 }, 2000); + setTimeout(function(){ + progressBar(0, $('#animation-Bar3'), 2000); + progressBar(0, $('#animation-Bar4'), 2000); + setTimeout(function(){cible = false}, 2005); + }, 2000); + + setTimeout(function(){ + arc('cible', false); + }, 4000); + + } else { + progressBar(100, $('#animation-Bar3'), 2000); + progressBar(100, $('#animation-Bar4'), 2000); + setTimeout(function(){ + cible = true; + $("#animation-myddleware-back-3 > .animation-myddleware-logo").animate({ width: 105 }, 2000) + }, 2005); + } + +} + +// Anime le Logo Myddleware (non utilisée pour l'instant) +var logo = false; +function animateLogo() { + if(logo){ + clearInterval(logoAnimation); + $("#animation-myddleware-back-1").fadeIn(500); + logo = false; + } else { + logoAnimation = setInterval(function() { + $("#animation-myddleware-back-1").fadeIn(500); + $("#animation-myddleware-back-1").fadeOut(500); + }, 1000); + logo = true; + } +} + +// Function de positionnement du logo Myddleware +function posMyddleware() { + + var pos = $("#animation-myddleware-div").position(); + if(typeof pos !== "undefined" ) { + //show the menu directly over the placeholder + $("#animation-myddleware-div > div").css({ + position: "absolute", + top: (pos.top - 50 ) + "px", + left: (pos.left + 30) + "px" + }).show(); + } + + +} + +// Affiche ou désaffiche le carrousel gauche (Responsive) +var content_left = false; +function content_left_toggle(e) { + if(content_left){ + $(".animation-content-left").animate({ left: -80 }, 1000); + } else { + $(".animation-content-left").animate({ left: 0 }, 1000); + } +} + +// Affiche ou désaffiche le carrousel droite (Responsive) +var content_right = false; +function content_right_toggle(e) { + if(content_right){ + $(".animation-content-right").animate({ right: -80 }, 1000); + } else { + $(".animation-content-right").animate({ right: 0 }, 1000); + } +} + +// Anime la progressBar dont l'ID est passé en paramètre +function progressBar(percent, $element, time) { + var progressBarWidth = percent * $element.width() / 100; + $element.find('div').animate({ width: progressBarWidth }, time); +} + + +// Grayscale w canvas method +function grayscale(src){ + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + var imgObj = new Image(); + imgObj.src = src; + canvas.width = imgObj.width; + canvas.height = imgObj.height; + ctx.drawImage(imgObj, 0, 0); + var imgPixels = ctx.getImageData(0, 0, canvas.width, canvas.height); + + for(var y = 0; y < imgPixels.height; y++){ + for(var x = 0; x < imgPixels.width; x++){ + var i = (y * 4) * imgPixels.width + x * 4; + var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3; + imgPixels.data[i] = avg; + imgPixels.data[i + 1] = avg; + imgPixels.data[i + 2] = avg; + } + } + ctx.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height); + return canvas.toDataURL(); +} diff --git a/src/Myddleware/RegleBundle/Resources/public/js/connector.js b/src/Myddleware/RegleBundle/Resources/public/js/connector.js new file mode 100644 index 000000000..8912840e9 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/connector.js @@ -0,0 +1,177 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +$( document ).ready(function() { + + // Sauvegarde du connecteur + $('#connector_save','#connector').click(function(){ + params = []; + $('.params','#tab_connector').each(function(){ + id = $.trim( $(this).attr('data-id') ); + value = $.trim( $(this).val() ); + + params.push( {value: value, id: id} ); + }); + + $.ajax({ + type: "POST", + url: path_connector_save, + data:{ + params : params, + nom : $('#label').val() + }, + success: function(data){ + if(data == 0) { + $('#validation').html(''); + } + else { + $('#validation').html(''); + window.location.href = data; + } + } + }); + + }); + + // Test connexion + $('#connexion').click(function(){ + var datas = ''; + var parent = 'source'; + var status = $('#source_status img'); + + $( '.title' ).each(function(){ + + if( $(this).text() != 'solution' && $(this).text() != 'nom') { + input = $('.params', $(this).parent()); + datas += $(this).text() + "::" + input.val() + ";"; + } + }); + + $.ajax({ + type: "POST", + url: inputs, + data:{ + champs : datas, + parent : parent, + solution : $('.vignette').attr('alt'), + mod : 2 // connexion + }, + beforeSend: function() { + status.removeAttr("src"); + status.attr("src",path_img + "loader.gif"); + }, + success: function(data){ + + r = data.split(';'); + // Si connexion echoue + if(r[1] == 0) { + status.removeAttr("src"); + status.attr("src",path_img + "status_offline.png"); + $('#msg_status span.error').html(r[0]); + $('#msg_status').show(); + return false; + } + // Si connexion ok alors on test si la solution à besoin de la pop-up + $.ajax({ + type: "POST", + data:{ + solutionjs : true, + detectjs : true + }, + url: callback, + success: function(data){ + param = data.split(';'); + + // si popup + if(param[0] == 1) { + + link = param[1]; + + $.ajax({ + type: "POST", + data:{ + solutionjs : true + }, + url: callback, + success: function(data){ + + // if 1ere fois + if(data != 1) { + + var win = window.open( link, 'Connexion','scrollbars=1,resizable=1,height=560,width=770'); + if(data != 401) { + var timer = setInterval(function() { + if(win.closed) { + clearInterval(timer); + if (confirm("Reconnect")) { + $('#connexion').click(); + } + } + }, 1000); + } + else { + $('#connexion').click(); + } + + r = data.split(';'); + + status.removeAttr("src"); + status.attr("src",path_img+"status_offline.png"); + $('#msg_status span.error').html(r[0]); + $('#msg_status').show(); + } + else { + status.removeAttr("src"); + status.attr("src",path_img+"status_online.png"); + $('#msg_status').hide(); + $('#msg_status span.error').html(''); + $('#step_modules_confirme').removeAttr('disabled'); + } + } + }); + }// sans popup + else { + if(r[1] == 0) { + status.removeAttr("src"); + status.attr("src",path_img+"status_offline.png"); + $('#msg_status span.error').html(r[0]); + $('#msg_status').show(); + } + else{ + status.removeAttr("src"); + status.attr("src",path_img+"status_online.png"); + $('#msg_status').hide(); + $('#msg_status span.error').html(''); + } + } + + + } + }); + + } + }); + }); + +}); \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/js/fiche.js b/src/Myddleware/RegleBundle/Resources/public/js/fiche.js new file mode 100644 index 000000000..b27723907 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/fiche.js @@ -0,0 +1,182 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stéphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stéphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +$( document ).ready(function() { + // ----------------------------- Fiche rule + + //$( ".mapping p" ).hide(); + + $( ".mapping > .title" ).click(function() { + $('p',$( this ).parent()).toggle( "fadein" ); + }); + + $( ".mapping > .title" ).click(function(){ + if($(this).parent().attr('class') == 'mapping') { + $(this).parent().attr('class','mapping gray'); + $(this).each(function(){ + if($(this).attr('data-title') == 'gray') { + $(this).attr('class','title'); + } + }); + } + else { + $(this).parent().attr('class','mapping'); + $(this).each(function(){ + if($(this).attr('data-title') == 'gray') { + $(this).attr('class','title gray'); + } + }); + } + }); + +// Paramètres ------------------------------------------------------------- + + $( "#activerule" ).click(function() { + $.ajax({ + type: "POST", + url: path_fiche_update, + success: function(data){ + } + }); + }); + + $.datepicker.regional['fr'] = { + closeText: 'Fermer', + prevText: 'Précédent', + nextText: 'Suivant', + currentText: 'Aujourd\'hui', + monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', + 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'], + monthNamesShort: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', + 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'], + dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], + dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], + dayNamesMin: ['D','L','M','M','J','V','S'], + weekHeader: 'Sem.', + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: ''}; + $.datepicker.setDefaults($.datepicker.regional['fr']); + + $('#datereference, .calendar').datetimepicker({ + timeFormat: 'HH:mm:ss', + dateFormat: 'yy-mm-dd', + timeText: 'Heure', + hourText: 'Heure', + minuteText: 'Minute', + secondText: 'Sconde', + currentText: 'Maintenant', + closeText: 'Fermer' + }); + + $( '#saveRuleParams' ).click(function() { + loading_img = $('#myd_loading_img', '.myd_loading'); + $.ajax({ + type: "POST", + url: path_fiche_update_params, + data:{ + params : recup_params(), + }, + beforeSend: function() { + $('#saveRuleParams').find('span').attr('class','glyphicon glyphicon-edit'); + loading_img.attr('src', path_img+'loader.gif'); + loading_img.show(); + }, + success: function(data){ + loading_img.hide(); + loading_img.replaceWith(""); + $('.myd_loading > span').show(); + if(data == 1) { + $('#saveRuleParams').find('span').attr('class','glyphicon glyphicon-ok-circle'); + } + else { + $('#saveRuleParams').find('span').attr('class','glyphicon glyphicon-remove-circle'); + } + setTimeout(function() { + $('.myd_loading > span').hide(); + $('.myd_loading > span').replaceWith(loading_img); + $('#saveRuleParams').find('span').attr('class','glyphicon glyphicon-edit'); + }, 2000); + } + }); + }); + + $( '#simuleRuleFluxAction' ).click(function() { + simule_img = $('#simuleRuleFluxLoading_img', '.simuleRuleFluxLoading'); + $.ajax({ + type: "GET", + url: path_fiche_update_simulate, + beforeSend: function() { + simule_img.attr('src', path_img+'loader.gif'); + simule_img.show(); + $('#simuleRuleFluxError').empty(); + $('#simuleRuleFluxError').hide(); + $('#simuleRuleFluxResult').empty(); + $('#simuleRuleFluxResult').append('-'); + }, + success: function(data){ + obj = jQuery.parseJSON( data ); + simule_img.hide(); + simule_img.replaceWith(""); + $('.simuleRuleFluxLoading > span').show(); + if(typeof obj.error === "undefined") { + $('#simuleRuleFlux').find('span').attr('class','glyphicon glyphicon-ok-circle'); + $('#simuleRuleFluxResult').empty(); + $('#simuleRuleFluxResult').append(data); + } else { + $('#simuleRuleFluxError').show(); + $('#simuleRuleFluxError').append(obj.error); + $('#simuleRuleFlux').find('span').attr('class','glyphicon glyphicon-remove-circle'); + } + setTimeout(function() { + $('.simuleRuleFluxLoading > span').hide(); + $('.simuleRuleFluxLoading > span').replaceWith(simule_img); + $('#simuleRuleFluxAction').find('span').attr('class','glyphicon glyphicon-play-circle'); + }, 2000); + } + }); + }); +}); + +// Récupère la liste des params +function recup_params() { + var params = []; + $( '.params','#ruleparams' ).each(function(){ + + name = $(this).attr('name'); + + if(name == 'datereference_txt') { + name = 'datereference'; + } + + value = $(this).val(); + id = $(this).attr('data-id'); + + params.push( {name: name, value: value, id: id } ); + }); + + return params; +} + diff --git a/src/Myddleware/RegleBundle/Resources/public/js/home.js b/src/Myddleware/RegleBundle/Resources/public/js/home.js new file mode 100644 index 000000000..fb0ad01bb --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/home.js @@ -0,0 +1,109 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +google.load("visualization", "1", {packages:["corechart"]}); +google.setOnLoadCallback(drawChart); +function drawChart() { + $.ajax({ + type: "POST", + url: path_graph_type_error_doc, + success: function(dataServ){ + var data = google.visualization.arrayToDataTable(JSON.parse(dataServ)); + var options = { + is3D: true, + }; + + var chart = new google.visualization.PieChart(document.getElementById('pie_chart_error_doc')); + chart.draw(data, options); + } + }); + + $.ajax({ + type: "POST", + url: path_graph_type_transfer_rule, + success: function(dataServ){ + var data = google.visualization.arrayToDataTable(JSON.parse(dataServ)); + var options = { + is3D: true, + }; + + var chart = new google.visualization.PieChart(document.getElementById('pie_chart_transfer_rule')); + chart.draw(data, options); + } + }); + + $.ajax({ + type: "POST", + url: path_graph_type_transfer_histo, + success: function(dataServ){ + var data = google.visualization.arrayToDataTable(JSON.parse(dataServ)); + var options = { + is3D: true, + isStacked: true, + }; + + var chart = new google.visualization.ColumnChart(document.getElementById('column_chart_histo')); + chart.draw(data, options); + } + }); +} +//-------------- + +$( document ).ready(function() { + + + if($('#listing-solutions','#panel').length != 0) { + $('#listing-solutions','#panel').scrollbox({ + direction: 'h', + distance: 65 + }); + $('#listing-solutions-backward','#panel').click(function () { + $('#listing-solutions','#panel').trigger('backward'); + }); + $('#listing-solutions-forward','#panel').click(function () { + $('#listing-solutions','#panel').trigger('forward'); + }); + $('#listing-solutions li','#panel').hover(function () { + string = $("img", this).attr('alt') + $("img", this).qtip({ + content: string.charAt(0).toUpperCase() + string.slice(1) + ": " + trans_click, + show: { + delay: 700, + ready: 'true', + }, + position: { + my: 'top center', // Position my top left... + at: 'bottom center', // at the bottom right of... + } + })}, + function () { + $('#listing-solutions img','#panel').each(function(){ + $(this).qtip("hide"); + }); + } + ); + } +}); + + diff --git a/src/Myddleware/RegleBundle/Resources/public/js/jcarousel.ajax.js b/src/Myddleware/RegleBundle/Resources/public/js/jcarousel.ajax.js new file mode 100644 index 000000000..e435f083d --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/jcarousel.ajax.js @@ -0,0 +1,555 @@ +/********************************************************************************* + * This file is part of Myddleware. + + * @package Myddleware + * @copyright Copyright (C) 2013 - 2015 Stphane Faure - CRMconsult EURL + * @copyright Copyright (C) 2015 - 2016 Stphane Faure - Myddleware ltd - contact@myddleware.com + * @link http://www.myddleware.com + + This file is part of Myddleware. + + Myddleware 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. + + Myddleware 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 Myddleware. If not, see . +*********************************************************************************/ + +!function ($) { + + var is, + transition; + + // from valentine + is = { + fun: function (f) { + return typeof f === 'function'; + }, + arr: function (ar) { + return ar instanceof Array; + }, + obj: function (o) { + return o instanceof Object && !is.fun(o) && !is.arr(o); + } + }; + + /* + Based on Bootstrap + Mozilla and Webkit support only + */ + transition = (function () { + var st = document.createElement('div').style, + transitionEnd = 'TransitionEnd', + transitionProp = 'Transition', + support = st.transition !== undefined || + st.WebkitTransition !== undefined || + st.MozTransition !== undefined; + + return support && { + prop: (function () { + if (st.WebkitTransition !== undefined) { + transitionProp = 'WebkitTransition'; + } else if (st.MozTransition !== undefined) { + transitionProp = 'MozTransition'; + } + return transitionProp; + }()), + end: (function () { + if (st.WebkitTransition !== undefined) { + transitionEnd = 'webkitTransitionEnd'; + } else if (st.MozTransition !== undefined) { + transitionEnd = 'transitionend'; + } + return transitionEnd; + }()) + }; + }()); + + function extend() { + // based on jQuery deep merge + var options, name, src, copy, clone, + target = arguments[0], i = 1, length = arguments.length; + + for (; i < length; i += 1) { + if ((options = arguments[i]) !== null) { + // Extend the base object + for (name in options) { + src = target[name]; + copy = options[name]; + if (target === copy) { + continue; + } + if (copy && (is.obj(copy))) { + clone = src && is.obj(src) ? src : {}; + target[name] = extend(clone, copy); + } else if (copy !== undefined) { + target[name] = copy; + } + } + } + } + return target; + } + + function clone(obj) { + if (null === obj || 'object' !== typeof obj) { + return obj; + } + var copy = obj.constructor(), + attr; + for (attr in obj) { + if (obj.hasOwnProperty(attr)) { + copy[attr] = obj[attr]; + } + } + return copy; + } + + // from jquery + function proxy(fn, context) { + var slice = Array.prototype.slice, + args = slice.call(arguments, 2); + return function () { + return fn.apply(context, args.concat(slice.call(arguments))); + }; + } + + function animate(options) { + var el = options.el, + complete = options.complete ? options.complete : function () {}, + animation, + dummy; + + // no animation obj OR animation is not available, + // fallback to css and call the callback + if (! options.animation || + ! (el.animate || (options.css3transition && transition))) { + el.css(options.fallbackCss); + complete(); + return; + } + + // we will animate, apply start CSS + if (options.animStartCss) { + if (options.animStartCss.opacity === 0) { + options.animStartCss.opacity = 0.01; // ie quirk + } + el.css(options.animStartCss); + } + animation = options.animation; + + // css3 setted, if available apply the css + if (options.css3transition && transition) { + dummy = el[0].offsetWidth; // force reflow; source: bootstrap + el[0].style[transition.prop] = 'all ' + animation.duration + 'ms'; + + // takaritas + delete animation.duration; + delete animation.easing; + + el.css(animation); + //el.unbind(transition.end); + el.on(transition.end, function () { + // delete transition properties and events + el.off(transition.end); + el[0].style[transition.prop] = 'none'; + complete(); + }); + } else if (window.ender) { + // use morpheus + el.animate(extend(animation, {'complete': complete})); + } else { + // use animate from jquery + el.animate(animation, animation.duration, animation.easing, complete); + } + } + + /* + Carousel Constructor + */ + function Carousel(el, options) { + this.init(el, options); + + // only return the API + // instead of this + return { + getPageSize: proxy(this.getPageSize, this), + getCursor: proxy(this.getCursor, this), + nextPage: proxy(this.nextPage, this), + prevPage: proxy(this.prevPage, this), + isVisibleItem: proxy(this.isVisibleItem, this), + scrollToItem: proxy(this.scrollToItem, this), + getOptions: proxy(this.getOptions, this), + setOptions: proxy(this.setOptions, this) + }; + } + + Carousel.prototype = { + init: function (el, options) { + var opt; + + this.options = { + window: '.window', + items: 'li', + pager: null, + nextPager: 'a.next', + prevPager: 'a.prev', + activeClass: null, + disabledClass: 'disabled', + duration: 400, + vertical: false, + keyboard: false, + css3transition: false, + extraOffset: 0 + }; + this.setOptions(options); + opt = this.options; + + if (opt.css3transition && ! transition) { + opt.css3transition = false; + } + + this.$el = $(el); + this.$window = this.$el.find( + opt.window + ); + this.$itemWrapper = this.$window.children().first(); + this.$items = this.$el.find( + opt.items + ); + this.$nextPager = this.$el.find( + opt.nextPager + ); + this.$prevPager = this.$el.find( + opt.prevPager + ); + + this.setDimensions(); + + if (opt.pager) { + this.$pager = this.$el.find( + opt.pager + ); + + this.createPager(); + + this.$pagerItems = this.$pager.find('li'); + } + + if (this.$items.length <= this.pageSize) { + this.hidePrevPager(); + this.hideNextPager(); + return; + } + + this.cursor = this.getActiveIndex(); + + if (this.cursor < 0) { + if (this.options.activeClass) { + for (var i = 0; i < this.pageSize; i += 1) { + $(this.$items.get(i)).addClass('active'); + } + } + + this.cursor = 0; + } + + if (this.cursor > this.lastPosition) { + this.cursor = this.lastPosition; + } + + if (this.cursor > 0) { + this.scrollToItem(this.cursor, true); + } + + if (this.cursor === 0) { + this.hidePrevPager(); + } + + if (this.cursor >= this.lastPosition) { + this.hideNextPager(); + } + + this.$nextPager.on('click', proxy(this.nextPage, this)); + this.$prevPager.on('click', proxy(this.prevPage, this)); + + if (opt.keyboard) { + $(document).on('keyup', proxy(this.onKeyUp, this)); + } + + this.$el.addClass('carousel-inited'); + }, + + setDimensions: function () { + var $secondItem, + alignedDimension = 'width', + marginType = ['margin-left', 'margin-right']; + + if (this.options.vertical) { + alignedDimension = 'height'; + marginType = ['margin-top', 'margin-bottom']; + } + + $secondItem = this.$items.first().next(); + this.itemMargin = parseInt($secondItem.css(marginType[0]), 10) + + parseInt($secondItem.css(marginType[1]), 10); + this.itemDimension = $secondItem[alignedDimension]() + this.itemMargin; + + this.windowDimension = this.$window[alignedDimension](); + this.pageSize = Math.floor( + (this.windowDimension + this.itemMargin) / this.itemDimension + ); + this.pageDimension = this.pageSize * this.itemDimension; + this.lastPosition = this.$items.length - this.pageSize; + + }, + + createPager: function () { + var itemsLen = this.$items.length, + pagerItemsFrag = document.createDocumentFragment(), + pagerItem, + i; + + for (i = 0; i < itemsLen; i += 1) { + pagerItem = document.createElement('li'); + $pagerItem = $(pagerItem); + + $pagerItem.on('click', proxy(this.usePager, this, i, itemsLen)); + + if (i < this.pageSize) { + $pagerItem.addClass('active'); + } + + pagerItemsFrag.appendChild(pagerItem); + } + + this.$pager.empty().get(0).appendChild(pagerItemsFrag); + }, + + usePager: function (pos, len) { + if (pos > (len - this.pageSize)) { + this.scrollToItem(len - this.pageSize); + } else { + this.scrollToItem(pos); + } + }, + + nextPage: function (e) { + if (typeof(e) !== 'undefined') { + e.preventDefault(); + } + + if (this.cursor >= this.lastPosition) { + return; + } + + var itemIdx = this.cursor + this.pageSize; + if (itemIdx > this.lastPosition) { + itemIdx = this.lastPosition; + } + + this.scrollToItem(itemIdx); + }, + + prevPage: function (e) { + if (typeof(e) !== 'undefined') { + e.preventDefault(); + } + + if (this.cursor === 0) { + return; + } + + var itemIdx = this.cursor - this.pageSize; + if (itemIdx < 0) { + itemIdx = 0; + } + + this.scrollToItem(itemIdx); + }, + + nextItem: function () { + if (this.cursor >= this.lastPosition) { + return; + } + + this.scrollToItem(this.cursor + 1); + }, + + prevItem: function () { + if (this.cursor === 0) { + return; + } + this.scrollToItem(this.cursor - 1); + }, + + scrollToItem: function (idx, doNotAnimate) { + var animateTo, + scrollTo, + direction = this.options.vertical ? 'top' : 'left', + animObj = {}, + activeClassName = this.options.activeClass || 'active', + itemsLen = this.$items.length, + i; + + this.cursorPrevious = this.cursor; + this.cursor = idx; + + if (this.cursor === 0) { + this.hidePrevPager(); + } else { + this.showPrevPager(); + } + + if (this.cursor >= this.lastPosition) { + this.hideNextPager(); + } else { + this.showNextPager(); + } + + scrollTo = this.cursor * this.itemDimension; + if (this.cursor === this.lastPosition) { + scrollTo = scrollTo - + (this.windowDimension - this.pageDimension + this.itemMargin) + + this.options.extraOffset; + } + + scrollTo *= -1; + animObj[direction] = scrollTo; + + if (! doNotAnimate) { + animObj.duration = this.options.duration; + } + + if (this.options.activeClass) { + activeClass = this.options.activeClass; + + if (this.getPageSize() === 1) { + $(this.$items.get(this.cursorPrevious)).removeClass(activeClass); + $(this.$items.get(idx)).addClass(activeClass); + } else { + itemslen = this.$items.length; + this.$items.removeClass(activeClass); + + for (i = 0; i < itemslen; i += 1) { + if (this.isVisibleItem(i)) { + $(this.$items.get(i)).addClass(activeClass); + } + } + } + } + + if (this.options.pager) { + if (this.getPageSize() === 1) { + $(this.$pagerItems.get(this.cursorPrevious)).removeClass(activeClassName); + $(this.$pagerItems.get(this.cursor)).addClass(activeClassName); + } else { + this.$pagerItems.removeClass(activeClassName); + + for (i = 0; i < itemsLen; i += 1) { + if (this.isVisibleItem(i)) { + $(this.$pagerItems.get(i)).addClass(activeClassName); + } + } + } + } + + animate({ + el: this.$itemWrapper, + animation: doNotAnimate ? false : animObj, + fallbackCss: animObj, + css3transition: this.options.css3transition + }); + }, + + onKeyUp: function (e) { + if (e.keyCode === 39) { + this.nextPage(); + } else if (e.keyCode === 37) { + this.prevPage(); + } + }, + + getActiveIndex: function () { + var i = 0, + il = this.$items.length; + + for (; i < il; i += 1) { + if ($(this.$items.get(i)).hasClass('active')) { + return i; + } + } + + return -1; + }, + + hideNextPager: function () { + this.$nextPager.addClass( + this.options.disabledClass + ); + }, + + hidePrevPager: function () { + this.$prevPager.addClass( + this.options.disabledClass + ); + }, + + showNextPager: function () { + this.$nextPager.removeClass( + this.options.disabledClass + ); + }, + + showPrevPager: function () { + this.$prevPager.removeClass( + this.options.disabledClass + ); + }, + + getPageSize: function () { + return this.pageSize; + }, + + getCursor: function () { + return this.cursor; + }, + + isVisibleItem: function (idx) { + if (this.cursor + this.pageSize <= idx || this.cursor > idx) { + return false; + } + return true; + }, + + getOptions: function () { + return this.options; + }, + + setOptions: function (options) { + extend(this.options, options || {}); + } + }; + + $.fn.carousel = function (options) { + return new Carousel(this.first(), options); + }; +}(window.ender || window.jQuery || window.Zepto); + +window.onload = function () { + $("#animation-cleft").carousel({ + vertical: true, + duration: 1000 + }); + $("#animation-cright").carousel({ + vertical: true, + duration: 1000 + }); +}; \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/js/lib/d3.v2.js b/src/Myddleware/RegleBundle/Resources/public/js/lib/d3.v2.js new file mode 100644 index 000000000..44fc0b072 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/lib/d3.v2.js @@ -0,0 +1,7026 @@ +(function() { + function d3_class(ctor, properties) { + try { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } catch (e) { + ctor.prototype = properties; + } + } + function d3_arrayCopy(pseudoarray) { + var i = -1, n = pseudoarray.length, array = []; + while (++i < n) array.push(pseudoarray[i]); + return array; + } + function d3_arraySlice(pseudoarray) { + return Array.prototype.slice.call(pseudoarray); + } + function d3_Map() {} + function d3_identity(d) { + return d; + } + function d3_this() { + return this; + } + function d3_true() { + return true; + } + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return arguments.length ? target : value; + }; + } + function d3_number(x) { + return x != null && !isNaN(x); + } + function d3_zipLength(d) { + return d.length; + } + function d3_splitter(d) { + return d == null; + } + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + function d3_dispatch() {} + function d3_dispatch_event(dispatch) { + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + var listeners = [], listenerByName = new d3_Map; + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + function d3_format_precision(x, p) { + return p - (x ? 1 + Math.floor(Math.log(x + Math.pow(10, 1 + Math.floor(Math.log(x) / Math.LN10) - p)) / Math.LN10) : 1); + } + function d3_format_typeDefault(x) { + return x + ""; + } + function d3_format_group(value) { + var i = value.lastIndexOf("."), f = i >= 0 ? value.substring(i) : (i = value.length, ""), t = []; + while (i > 0) t.push(value.substring(i -= 3, i + 3)); + return t.reverse().join(",") + f; + } + function d3_formatPrefix(d, i) { + var k = Math.pow(10, Math.abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_identity(t) { + return t; + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * Math.PI / 2); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length < 1) { + a = 1; + s = p / 4; + } else s = p / (2 * Math.PI) * Math.asin(1 / a); + return function(t) { + return 1 + a * Math.pow(2, 10 * -t) * Math.sin((t - s) * 2 * Math.PI / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + function d3_eventCancel() { + d3.event.stopPropagation(); + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch, i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_transformDegrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_transformDegrees : 0; + } + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + function d3_interpolateByName(name) { + return name == "transform" ? d3.interpolateTransform : d3.interpolate; + } + function d3_uninterpolateNumber(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return (x - a) * b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return Math.max(0, Math.min(1, (x - a) * b)); + }; + } + function d3_Color() {} + function d3_rgb(r, g, b) { + return new d3_Rgb(r, g, b); + } + function d3_Rgb(r, g, b) { + this.r = r; + this.g = g; + this.b = b; + } + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, name; + m1 = /([a-z]+)\((.*)\)/i.exec(format); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b); + if (format != null && format.charAt(0) === "#") { + if (format.length === 4) { + r = format.charAt(1); + r += r; + g = format.charAt(2); + g += g; + b = format.charAt(3); + b += b; + } else if (format.length === 7) { + r = format.substring(1, 3); + g = format.substring(3, 5); + b = format.substring(5, 7); + } + r = parseInt(r, 16); + g = parseInt(g, 16); + b = parseInt(b, 16); + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + s = h = 0; + } + return d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + function d3_hsl(h, s, l) { + return new d3_Hsl(h, s, l); + } + function d3_Hsl(h, s, l) { + this.h = h; + this.s = s; + this.l = l; + } + function d3_hsl_rgb(h, s, l) { + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + var m1, m2; + h = h % 360; + if (h < 0) h += 360; + s = s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + function d3_hcl(h, c, l) { + return new d3_Hcl(h, c, l); + } + function d3_Hcl(h, c, l) { + this.h = h; + this.c = c; + this.l = l; + } + function d3_hcl_lab(h, c, l) { + return d3_lab(l, Math.cos(h *= Math.PI / 180) * c, Math.sin(h) * c); + } + function d3_lab(l, a, b) { + return new d3_Lab(l, a, b); + } + function d3_Lab(l, a, b) { + this.l = l; + this.a = a; + this.b = b; + } + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return d3_hcl(Math.atan2(b, a) / Math.PI * 180, Math.sqrt(a * a + b * b), l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + function d3_selection(groups) { + d3_arraySubclass(groups, d3_selectionPrototype); + return groups; + } + function d3_selection_selector(selector) { + return function() { + return d3_select(selector, this); + }; + } + function d3_selection_selectorAll(selector) { + return function() { + return d3_selectAll(selector, this); + }; + } + function d3_selection_attr(name, value) { + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + name = d3.ns.qualify(name); + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classed(name, value) { + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + name = name.trim().split(/\s+/).map(d3_selection_classedName); + var n = name.length; + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.className, cb = c.baseVal != null, cv = cb ? c.baseVal : c; + if (value) { + re.lastIndex = 0; + if (!re.test(cv)) { + cv = d3_collapse(cv + " " + name); + if (cb) c.baseVal = cv; else node.className = cv; + } + } else if (cv) { + cv = d3_collapse(cv.replace(re, " ")); + if (cb) c.baseVal = cv; else node.className = cv; + } + }; + } + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3.ascending; + return function(a, b) { + return comparator(a && a.__data__, b && b.__data__); + }; + } + function d3_selection_on(type, listener, capture) { + function onRemove() { + var wrapper = this[name]; + if (wrapper) { + this.removeEventListener(type, wrapper, wrapper.$); + delete this[name]; + } + } + function onAdd() { + function wrapper(e) { + var o = d3.event; + d3.event = e; + args[0] = node.__data__; + try { + listener.apply(node, args); + } finally { + d3.event = o; + } + } + var node = this, args = arguments; + onRemove.call(this); + this.addEventListener(type, this[name] = wrapper, wrapper.$ = capture); + wrapper._ = listener; + } + var name = "__on" + type, i = type.indexOf("."); + if (i > 0) type = type.substring(0, i); + return listener ? onAdd : onRemove; + } + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + function d3_selection_enter(selection) { + d3_arraySubclass(selection, d3_selection_enterPrototype); + return selection; + } + function d3_transition(groups, id, time) { + d3_arraySubclass(groups, d3_transitionPrototype); + var tweens = new d3_Map, event = d3.dispatch("start", "end"), ease = d3_transitionEase; + groups.id = id; + groups.time = time; + groups.tween = function(name, tween) { + if (arguments.length < 2) return tweens.get(name); + if (tween == null) tweens.remove(name); else tweens.set(name, tween); + return groups; + }; + groups.ease = function(value) { + if (!arguments.length) return ease; + ease = typeof value === "function" ? value : d3.ease.apply(d3, arguments); + return groups; + }; + groups.each = function(type, listener) { + if (arguments.length < 2) return d3_transition_each.call(groups, type); + event.on(type, listener); + return groups; + }; + d3.timer(function(elapsed) { + return d3_selection_each(groups, function(node, i, j) { + function start(elapsed) { + if (lock.active > id) return stop(); + lock.active = id; + tweens.forEach(function(key, value) { + if (value = value.call(node, d, i)) { + tweened.push(value); + } + }); + event.start.call(node, d, i); + if (!tick(elapsed)) d3.timer(tick, 0, time); + return 1; + } + function tick(elapsed) { + if (lock.active !== id) return stop(); + var t = (elapsed - delay) / duration, e = ease(t), n = tweened.length; + while (n > 0) { + tweened[--n].call(node, e); + } + if (t >= 1) { + stop(); + d3_transitionId = id; + event.end.call(node, d, i); + d3_transitionId = 0; + return 1; + } + } + function stop() { + if (!--lock.count) delete node.__transition__; + return 1; + } + var tweened = [], delay = node.delay, duration = node.duration, lock = (node = node.node).__transition__ || (node.__transition__ = { + active: 0, + count: 0 + }), d = node.__data__; + ++lock.count; + delay <= elapsed ? start(elapsed) : d3.timer(start, delay, time); + }); + }, 0, time); + return groups; + } + function d3_transition_each(callback) { + var id = d3_transitionId, ease = d3_transitionEase, delay = d3_transitionDelay, duration = d3_transitionDuration; + d3_transitionId = this.id; + d3_transitionEase = this.ease(); + d3_selection_each(this, function(node, i, j) { + d3_transitionDelay = node.delay; + d3_transitionDuration = node.duration; + callback.call(node = node.node, node.__data__, i, j); + }); + d3_transitionId = id; + d3_transitionEase = ease; + d3_transitionDelay = delay; + d3_transitionDuration = duration; + return this; + } + function d3_tweenNull(d, i, a) { + return a != "" && d3_tweenRemove; + } + function d3_tweenByName(b, name) { + return d3.tween(b, d3_interpolateByName(name)); + } + function d3_timer_step() { + var elapsed, now = Date.now(), t1 = d3_timer_queue; + while (t1) { + elapsed = now - t1.then; + if (elapsed >= t1.delay) t1.flush = t1.callback(elapsed); + t1 = t1.next; + } + var delay = d3_timer_flush() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + function d3_timer_flush() { + var t0 = null, t1 = d3_timer_queue, then = Infinity; + while (t1) { + if (t1.flush) { + delete d3_timer_byId[t1.callback.id]; + t1 = t0 ? t0.next = t1.next : d3_timer_queue = t1.next; + } else { + then = Math.min(then, t1.then + t1.delay); + t1 = (t0 = t1).next; + } + } + return then; + } + function d3_mousePoint(container, e) { + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0 && (window.scrollX || window.scrollY)) { + svg = d3.select(document.body).append("svg").style("position", "absolute").style("top", 0).style("left", 0); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + if (d3_mouse_bug44083) { + point.x = e.pageX; + point.y = e.pageY; + } else { + point.x = e.clientX; + point.y = e.clientY; + } + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + function d3_noop() {} + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + if (nice = nice(x1 - x0)) { + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + } + return domain; + } + function d3_scale_niceDefault() { + return Math; + } + function d3_scale_linear(domain, range, interpolate, clamp) { + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3.interpolate); + return scale; + } + function scale(x) { + return output(x); + } + var output, input; + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3.interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m) { + return d3_scale_linearTickFormat(domain, m); + }; + scale.nice = function() { + d3_scale_nice(domain, d3_scale_linearNice); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(dx) { + dx = Math.pow(10, Math.round(Math.log(dx) / Math.LN10) - 1); + return dx && { + floor: function(x) { + return Math.floor(x / dx) * dx; + }, + ceil: function(x) { + return Math.ceil(x / dx) * dx; + } + }; + } + function d3_scale_linearTickRange(domain, m) { + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m) { + return d3.format(",." + Math.max(0, -Math.floor(Math.log(d3_scale_linearTickRange(domain, m)[2]) / Math.LN10 + .01)) + "f"); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + function d3_scale_log(linear, log) { + function scale(x) { + return linear(log(x)); + } + var pow = log.pow; + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(pow); + log = x[0] < 0 ? d3_scale_logn : d3_scale_logp; + pow = log.pow; + linear.domain(x.map(log)); + return scale; + }; + scale.nice = function() { + linear.domain(d3_scale_nice(linear.domain(), d3_scale_niceDefault)); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(linear.domain()), ticks = []; + if (extent.every(isFinite)) { + var i = Math.floor(extent[0]), j = Math.ceil(extent[1]), u = pow(extent[0]), v = pow(extent[1]); + if (log === d3_scale_logn) { + ticks.push(pow(i)); + for (; i++ < j; ) for (var k = 9; k > 0; k--) ticks.push(pow(i) * k); + } else { + for (; i < j; i++) for (var k = 1; k < 10; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (arguments.length < 2) format = d3_scale_logFormat; + if (arguments.length < 1) return format; + var k = Math.max(.1, n / scale.ticks().length), f = log === d3_scale_logn ? (e = -1e-12, Math.floor) : (e = 1e-12, Math.ceil), e; + return function(d) { + return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), log); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_logp(x) { + return Math.log(x < 0 ? 0 : x) / Math.LN10; + } + function d3_scale_logn(x) { + return -Math.log(x > 0 ? 0 : -x) / Math.LN10; + } + function d3_scale_pow(linear, exponent) { + function scale(x) { + return linear(powp(x)); + } + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(powb); + linear.domain(x.map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(scale.domain(), m); + }; + scale.tickFormat = function(m) { + return d3_scale_linearTickFormat(scale.domain(), m); + }; + scale.nice = function() { + return scale.domain(d3_scale_nice(scale.domain(), d3_scale_linearNice)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + var domain = scale.domain(); + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + return scale.domain(domain); + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + function d3_scale_ordinal(domain, ranger) { + function scale(x) { + return range[((index.get(x) || index.set(x, domain.push(x))) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + var index, range, rangeBand; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map; + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding); + range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step; + range = steps(start + Math.round(error / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + function d3_scale_quantile(domain, range) { + function rescale() { + var k = 0, n = domain.length, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (isNaN(x = +x)) return NaN; + return range[d3.bisect(thresholds, x)]; + } + var thresholds; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.filter(function(d) { + return !isNaN(d); + }).sort(d3.ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + function d3_scale_quantize(x0, x1, range) { + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + var kx, i; + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + function d3_scale_threshold(domain, range) { + function scale(x) { + return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m) { + return d3_scale_linearTickFormat(domain, m); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_line(projection) { + function line(data) { + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + var x = d3_svg_lineX, y = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + function d3_svg_lineX(d) { + return d[0]; + } + function d3_svg_lineY(d) { + return d[1]; + } + function d3_svg_lineLinear(points) { + return points.join("L"); + } + function d3_svg_lineLinearClosed(points) { + return d3_svg_lineLinear(points) + "Z"; + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension, closed) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0 ]; + d3_svg_lineBasisBezier(path, px, py); + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + i = -1; + while (++i < 2) { + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (Math.abs(d) < 1e-6) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] + d3_svg_arcOffset; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + function area(data) { + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + var x0 = d3_svg_lineX, x1 = d3_svg_lineX, y0 = 0, y1 = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + function d3_svg_chordSource(d) { + return d.source; + } + function d3_svg_chordTarget(d) { + return d.target; + } + function d3_svg_chordRadius(d) { + return d.radius; + } + function d3_svg_chordStartAngle(d) { + return d.startAngle; + } + function d3_svg_chordEndAngle(d) { + return d.endAngle; + } + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / Math.PI); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + function d3_svg_axisX(selection, x) { + selection.attr("transform", function(d) { + return "translate(" + x(d) + ",0)"; + }); + } + function d3_svg_axisY(selection, y) { + selection.attr("transform", function(d) { + return "translate(0," + y(d) + ")"; + }); + } + function d3_svg_axisSubdivide(scale, ticks, m) { + subticks = []; + if (m && ticks.length > 1) { + var extent = d3_scaleExtent(scale.domain()), subticks, i = -1, n = ticks.length, d = (ticks[1] - ticks[0]) / ++m, j, v; + while (++i < n) { + for (j = m; --j > 0; ) { + if ((v = +ticks[i] - j * d) >= extent[0]) { + subticks.push(v); + } + } + } + for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1]; ) { + subticks.push(v); + } + } + return subticks; + } + function d3_behavior_zoomDelta() { + if (!d3_behavior_zoomDiv) { + d3_behavior_zoomDiv = d3.select("body").append("div").style("visibility", "hidden").style("top", 0).style("height", 0).style("width", 0).style("overflow-y", "scroll").append("div").style("height", "2000px").node().parentNode; + } + var e = d3.event, delta; + try { + d3_behavior_zoomDiv.scrollTop = 1e3; + d3_behavior_zoomDiv.dispatchEvent(e); + delta = 1e3 - d3_behavior_zoomDiv.scrollTop; + } catch (error) { + delta = e.wheelDelta || -e.detail * 5; + } + return delta; + } + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= 1; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + } + function d3_layout_forceMouseout(d) { + d.fixed &= 3; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + function d3_layout_forceLinkDistance(link) { + return 20; + } + function d3_layout_forceLinkStrength(link) { + return 1; + } + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (; i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.links = d3_layout_hierarchyLinks; + object.nodes = function(d) { + d3_layout_hierarchyInline = true; + return (object.nodes = object)(d); + }; + return object; + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return dr * dr - dx * dx - dy * dy > .001; + } + function d3_layout_packSiblings(node) { + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(node) { + var children = node.children; + return children && children.length ? children[0] : node._tree.thread; + } + function d3_layout_treeRight(node) { + var children = node.children, n; + return children && (n = children.length) ? children[n - 1] : node._tree.thread; + } + function d3_layout_treeSearch(node, compare) { + var children = node.children; + if (children && (n = children.length)) { + var child, n, i = -1; + while (++i < n) { + if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) { + node = child; + } + } + } + return node; + } + function d3_layout_treeRightmost(a, b) { + return a.x - b.x; + } + function d3_layout_treeLeftmost(a, b) { + return b.x - a.x; + } + function d3_layout_treeDeepest(a, b) { + return a.depth - b.depth; + } + function d3_layout_treeVisitAfter(node, callback) { + function visit(node, previousSibling) { + var children = node.children; + if (children && (n = children.length)) { + var child, previousChild = null, i = -1, n; + while (++i < n) { + child = children[i]; + visit(child, previousChild); + previousChild = child; + } + } + callback(node, previousSibling); + } + visit(node, null); + } + function d3_layout_treeShift(node) { + var shift = 0, change = 0, children = node.children, i = children.length, child; + while (--i >= 0) { + child = children[i]._tree; + child.prelim += shift; + child.mod += shift; + shift += child.shift + (change += child.change); + } + } + function d3_layout_treeMove(ancestor, node, shift) { + ancestor = ancestor._tree; + node = node._tree; + var change = shift / (node.number - ancestor.number); + ancestor.change += change; + node.change -= change; + node.shift += shift; + node.prelim += shift; + node.mod += shift; + } + function d3_layout_treeAncestor(vim, node, ancestor) { + return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor; + } + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + function d3_dsv(delimiter, mimeType) { + function dsv(url, callback) { + d3.text(url, mimeType, function(text) { + callback(text && dsv.parse(text)); + }); + } + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + var reParse = new RegExp("\r\n|[" + delimiter + "\r\n]", "g"), reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + dsv.parse = function(text) { + var header; + return dsv.parseRows(text, function(row, i) { + if (i) { + var o = {}, j = -1, m = header.length; + while (++j < m) o[header[j]] = row[j]; + return o; + } else { + header = row; + return null; + } + }); + }; + dsv.parseRows = function(text, f) { + function token() { + if (reParse.lastIndex >= text.length) return EOF; + if (eol) { + eol = false; + return EOL; + } + var j = reParse.lastIndex; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < text.length) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + i++; + } + } + reParse.lastIndex = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) reParse.lastIndex++; + } else if (c === 10) { + eol = true; + } + return text.substring(j + 1, i).replace(/""/g, '"'); + } + var m = reParse.exec(text); + if (m) { + eol = m[0].charCodeAt(0) !== delimiterCode; + return text.substring(j, m.index); + } + reParse.lastIndex = text.length; + return text.substring(j); + } + var EOL = {}, EOF = {}, rows = [], n = 0, t, eol; + reParse.lastIndex = 0; + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && !(a = f(a, n++))) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + return rows.map(formatRow).join("\n"); + }; + return dsv; + } + function d3_geo_type(types, defaultValue) { + return function(object) { + return object && types.hasOwnProperty(object.type) ? types[object.type](object) : defaultValue; + }; + } + function d3_path_circle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + +2 * radius + "z"; + } + function d3_geo_bounds(o, f) { + if (d3_geo_boundsTypes.hasOwnProperty(o.type)) d3_geo_boundsTypes[o.type](o, f); + } + function d3_geo_boundsFeature(o, f) { + d3_geo_bounds(o.geometry, f); + } + function d3_geo_boundsFeatureCollection(o, f) { + for (var a = o.features, i = 0, n = a.length; i < n; i++) { + d3_geo_bounds(a[i].geometry, f); + } + } + function d3_geo_boundsGeometryCollection(o, f) { + for (var a = o.geometries, i = 0, n = a.length; i < n; i++) { + d3_geo_bounds(a[i], f); + } + } + function d3_geo_boundsLineString(o, f) { + for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) { + f.apply(null, a[i]); + } + } + function d3_geo_boundsMultiLineString(o, f) { + for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) { + for (var b = a[i], j = 0, m = b.length; j < m; j++) { + f.apply(null, b[j]); + } + } + } + function d3_geo_boundsMultiPolygon(o, f) { + for (var a = o.coordinates, i = 0, n = a.length; i < n; i++) { + for (var b = a[i][0], j = 0, m = b.length; j < m; j++) { + f.apply(null, b[j]); + } + } + } + function d3_geo_boundsPoint(o, f) { + f.apply(null, o.coordinates); + } + function d3_geo_boundsPolygon(o, f) { + for (var a = o.coordinates[0], i = 0, n = a.length; i < n; i++) { + f.apply(null, a[i]); + } + } + function d3_geo_greatArcSource(d) { + return d.source; + } + function d3_geo_greatArcTarget(d) { + return d.target; + } + function d3_geo_greatArcInterpolator() { + function interpolate(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) / d3_geo_radians, Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians ]; + } + var x0, y0, cy0, sy0, kx0, ky0, x1, y1, cy1, sy1, kx1, ky1, d, k; + interpolate.distance = function() { + if (d == null) k = 1 / Math.sin(d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0))))); + return d; + }; + interpolate.source = function(_) { + var cx0 = Math.cos(x0 = _[0] * d3_geo_radians), sx0 = Math.sin(x0); + cy0 = Math.cos(y0 = _[1] * d3_geo_radians); + sy0 = Math.sin(y0); + kx0 = cy0 * cx0; + ky0 = cy0 * sx0; + d = null; + return interpolate; + }; + interpolate.target = function(_) { + var cx1 = Math.cos(x1 = _[0] * d3_geo_radians), sx1 = Math.sin(x1); + cy1 = Math.cos(y1 = _[1] * d3_geo_radians); + sy1 = Math.sin(y1); + kx1 = cy1 * cx1; + ky1 = cy1 * sx1; + d = null; + return interpolate; + }; + return interpolate; + } + function d3_geo_greatArcInterpolate(a, b) { + var i = d3_geo_greatArcInterpolator().source(a).target(b); + i.distance(); + return i; + } + function d3_geom_contourStart(grid) { + var x = 0, y = 0; + while (true) { + if (grid(x, y)) { + return [ x, y ]; + } + if (x === 0) { + x = y + 1; + y = 0; + } else { + x = x - 1; + y = y + 1; + } + } + } + function d3_geom_hullCCW(i1, i2, i3, v) { + var t, a, b, c, d, e, f; + t = v[i1]; + a = t[0]; + b = t[1]; + t = v[i2]; + c = t[0]; + d = t[1]; + t = v[i3]; + e = t[0]; + f = t[1]; + return (f - b) * (c - a) - (d - b) * (e - a) > 0; + } + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x2 = d[0], x3 = a[0], x4 = b[0], y1 = c[1], y2 = d[1], y3 = a[1], y4 = b[1], x13 = x1 - x3, x21 = x2 - x1, x43 = x4 - x3, y13 = y1 - y3, y21 = y2 - y1, y43 = y4 - y3, ua = (x43 * y13 - y43 * x13) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + function d3_voronoi_tessellate(vertices, callback) { + var Sites = { + list: vertices.map(function(v, i) { + return { + index: i, + x: v[0], + y: v[1] + }; + }).sort(function(a, b) { + return a.y < b.y ? -1 : a.y > b.y ? 1 : a.x < b.x ? -1 : a.x > b.x ? 1 : 0; + }), + bottomSite: null + }; + var EdgeList = { + list: [], + leftEnd: null, + rightEnd: null, + init: function() { + EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l"); + EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l"); + EdgeList.leftEnd.r = EdgeList.rightEnd; + EdgeList.rightEnd.l = EdgeList.leftEnd; + EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd); + }, + createHalfEdge: function(edge, side) { + return { + edge: edge, + side: side, + vertex: null, + l: null, + r: null + }; + }, + insert: function(lb, he) { + he.l = lb; + he.r = lb.r; + lb.r.l = he; + lb.r = he; + }, + leftBound: function(p) { + var he = EdgeList.leftEnd; + do { + he = he.r; + } while (he != EdgeList.rightEnd && Geom.rightOf(he, p)); + he = he.l; + return he; + }, + del: function(he) { + he.l.r = he.r; + he.r.l = he.l; + he.edge = null; + }, + right: function(he) { + return he.r; + }, + left: function(he) { + return he.l; + }, + leftRegion: function(he) { + return he.edge == null ? Sites.bottomSite : he.edge.region[he.side]; + }, + rightRegion: function(he) { + return he.edge == null ? Sites.bottomSite : he.edge.region[d3_voronoi_opposite[he.side]]; + } + }; + var Geom = { + bisect: function(s1, s2) { + var newEdge = { + region: { + l: s1, + r: s2 + }, + ep: { + l: null, + r: null + } + }; + var dx = s2.x - s1.x, dy = s2.y - s1.y, adx = dx > 0 ? dx : -dx, ady = dy > 0 ? dy : -dy; + newEdge.c = s1.x * dx + s1.y * dy + (dx * dx + dy * dy) * .5; + if (adx > ady) { + newEdge.a = 1; + newEdge.b = dy / dx; + newEdge.c /= dx; + } else { + newEdge.b = 1; + newEdge.a = dx / dy; + newEdge.c /= dy; + } + return newEdge; + }, + intersect: function(el1, el2) { + var e1 = el1.edge, e2 = el2.edge; + if (!e1 || !e2 || e1.region.r == e2.region.r) { + return null; + } + var d = e1.a * e2.b - e1.b * e2.a; + if (Math.abs(d) < 1e-10) { + return null; + } + var xint = (e1.c * e2.b - e2.c * e1.b) / d, yint = (e2.c * e1.a - e1.c * e2.a) / d, e1r = e1.region.r, e2r = e2.region.r, el, e; + if (e1r.y < e2r.y || e1r.y == e2r.y && e1r.x < e2r.x) { + el = el1; + e = e1; + } else { + el = el2; + e = e2; + } + var rightOfSite = xint >= e.region.r.x; + if (rightOfSite && el.side === "l" || !rightOfSite && el.side === "r") { + return null; + } + return { + x: xint, + y: yint + }; + }, + rightOf: function(he, p) { + var e = he.edge, topsite = e.region.r, rightOfSite = p.x > topsite.x; + if (rightOfSite && he.side === "l") { + return 1; + } + if (!rightOfSite && he.side === "r") { + return 0; + } + if (e.a === 1) { + var dyp = p.y - topsite.y, dxp = p.x - topsite.x, fast = 0, above = 0; + if (!rightOfSite && e.b < 0 || rightOfSite && e.b >= 0) { + above = fast = dyp >= e.b * dxp; + } else { + above = p.x + p.y * e.b > e.c; + if (e.b < 0) { + above = !above; + } + if (!above) { + fast = 1; + } + } + if (!fast) { + var dxs = topsite.x - e.region.l.x; + above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b); + if (e.b < 0) { + above = !above; + } + } + } else { + var yl = e.c - e.a * p.x, t1 = p.y - yl, t2 = p.x - topsite.x, t3 = yl - topsite.y; + above = t1 * t1 > t2 * t2 + t3 * t3; + } + return he.side === "l" ? above : !above; + }, + endPoint: function(edge, side, site) { + edge.ep[side] = site; + if (!edge.ep[d3_voronoi_opposite[side]]) return; + callback(edge); + }, + distance: function(s, t) { + var dx = s.x - t.x, dy = s.y - t.y; + return Math.sqrt(dx * dx + dy * dy); + } + }; + var EventQueue = { + list: [], + insert: function(he, site, offset) { + he.vertex = site; + he.ystar = site.y + offset; + for (var i = 0, list = EventQueue.list, l = list.length; i < l; i++) { + var next = list[i]; + if (he.ystar > next.ystar || he.ystar == next.ystar && site.x > next.vertex.x) { + continue; + } else { + break; + } + } + list.splice(i, 0, he); + }, + del: function(he) { + for (var i = 0, ls = EventQueue.list, l = ls.length; i < l && ls[i] != he; ++i) {} + ls.splice(i, 1); + }, + empty: function() { + return EventQueue.list.length === 0; + }, + nextEvent: function(he) { + for (var i = 0, ls = EventQueue.list, l = ls.length; i < l; ++i) { + if (ls[i] == he) return ls[i + 1]; + } + return null; + }, + min: function() { + var elem = EventQueue.list[0]; + return { + x: elem.vertex.x, + y: elem.ystar + }; + }, + extractMin: function() { + return EventQueue.list.shift(); + } + }; + EdgeList.init(); + Sites.bottomSite = Sites.list.shift(); + var newSite = Sites.list.shift(), newIntStar; + var lbnd, rbnd, llbnd, rrbnd, bisector; + var bot, top, temp, p, v; + var e, pm; + while (true) { + if (!EventQueue.empty()) { + newIntStar = EventQueue.min(); + } + if (newSite && (EventQueue.empty() || newSite.y < newIntStar.y || newSite.y == newIntStar.y && newSite.x < newIntStar.x)) { + lbnd = EdgeList.leftBound(newSite); + rbnd = EdgeList.right(lbnd); + bot = EdgeList.rightRegion(lbnd); + e = Geom.bisect(bot, newSite); + bisector = EdgeList.createHalfEdge(e, "l"); + EdgeList.insert(lbnd, bisector); + p = Geom.intersect(lbnd, bisector); + if (p) { + EventQueue.del(lbnd); + EventQueue.insert(lbnd, p, Geom.distance(p, newSite)); + } + lbnd = bisector; + bisector = EdgeList.createHalfEdge(e, "r"); + EdgeList.insert(lbnd, bisector); + p = Geom.intersect(bisector, rbnd); + if (p) { + EventQueue.insert(bisector, p, Geom.distance(p, newSite)); + } + newSite = Sites.list.shift(); + } else if (!EventQueue.empty()) { + lbnd = EventQueue.extractMin(); + llbnd = EdgeList.left(lbnd); + rbnd = EdgeList.right(lbnd); + rrbnd = EdgeList.right(rbnd); + bot = EdgeList.leftRegion(lbnd); + top = EdgeList.rightRegion(rbnd); + v = lbnd.vertex; + Geom.endPoint(lbnd.edge, lbnd.side, v); + Geom.endPoint(rbnd.edge, rbnd.side, v); + EdgeList.del(lbnd); + EventQueue.del(rbnd); + EdgeList.del(rbnd); + pm = "l"; + if (bot.y > top.y) { + temp = bot; + bot = top; + top = temp; + pm = "r"; + } + e = Geom.bisect(bot, top); + bisector = EdgeList.createHalfEdge(e, pm); + EdgeList.insert(llbnd, bisector); + Geom.endPoint(e, d3_voronoi_opposite[pm], v); + p = Geom.intersect(llbnd, bisector); + if (p) { + EventQueue.del(llbnd); + EventQueue.insert(llbnd, p, Geom.distance(p, bot)); + } + p = Geom.intersect(bisector, rrbnd); + if (p) { + EventQueue.insert(bisector, p, Geom.distance(p, bot)); + } + } else { + break; + } + } + for (lbnd = EdgeList.right(EdgeList.leftEnd); lbnd != EdgeList.rightEnd; lbnd = EdgeList.right(lbnd)) { + callback(lbnd.edge); + } + } + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + function d3_geom_quadtreePoint(p) { + return { + x: p[0], + y: p[1] + }; + } + function d3_time_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + function d3_time_formatAbbreviate(name) { + return name.substring(0, 3); + } + function d3_time_parse(date, template, string, j) { + var c, p, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c == 37) { + p = d3_time_parsers[template.charAt(i++)]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map, i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.substring(i)); + return n ? i += n[0].length : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.substring(i)); + return n ? i += n[0].length : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i += n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i += n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 4)); + return n ? (date.y = +n[0], i += n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i += n[0].length) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.m = n[0] - 1, i += n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.d = +n[0], i += n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.H = +n[0], i += n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.M = +n[0], i += n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.S = +n[0], i += n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 3)); + return n ? (date.L = +n[0], i += n[0].length) : -1; + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(Math.abs(z) / 60), zm = Math.abs(z) % 60; + return zs + d3_time_zfill2(zh) + d3_time_zfill2(zm); + } + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_time(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_time(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_time = d3_time_utc; + var utc = new d3_time_utc; + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_time = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_time = d3_time_utc; + var utc = new d3_time_utc; + utc._ = date; + return method(utc, k)._; + } finally { + d3_time = Date; + } + }; + } + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + scale.nice = function(m) { + return scale.domain(d3_scale_nice(scale.domain(), function() { + return m; + })); + }; + scale.ticks = function(m, k) { + var extent = d3_time_scaleExtent(scale.domain()); + if (typeof m !== "function") { + var span = extent[1] - extent[0], target = span / m, i = d3.bisect(d3_time_scaleSteps, target); + if (i == d3_time_scaleSteps.length) return methods.year(extent, m); + if (!i) return linear.ticks(m).map(d3_time_scaleDate); + if (Math.log(target / d3_time_scaleSteps[i - 1]) < Math.log(d3_time_scaleSteps[i] / target)) --i; + m = methods[i]; + k = m[1]; + m = m[0].range; + } + return m(extent[0], new Date(+extent[1] + 1), k); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_time_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_time_scaleDate(t) { + return new Date(t); + } + function d3_time_scaleFormat(formats) { + return function(date) { + var i = formats.length - 1, f = formats[i]; + while (!f[1](date)) f = formats[--i]; + return f[0](date); + }; + } + function d3_time_scaleSetYear(y) { + var d = new Date(y, 0, 1); + d.setFullYear(y); + return d; + } + function d3_time_scaleGetYear(d) { + var y = d.getFullYear(), d0 = d3_time_scaleSetYear(y), d1 = d3_time_scaleSetYear(y + 1); + return y + (d - d0) / (d1 - d0); + } + function d3_time_scaleUTCSetYear(y) { + var d = new Date(Date.UTC(y, 0, 1)); + d.setUTCFullYear(y); + return d; + } + function d3_time_scaleUTCGetYear(d) { + var y = d.getUTCFullYear(), d0 = d3_time_scaleUTCSetYear(y), d1 = d3_time_scaleUTCSetYear(y + 1); + return y + (d - d0) / (d1 - d0); + } + if (!Date.now) Date.now = function() { + return +(new Date); + }; + try { + document.createElement("div").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_style_prototype = CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + d3 = { + version: "2.10.3" + }; + var d3_array = d3_arraySlice; + try { + d3_array(document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = d3_arrayCopy; + } + var d3_arraySubclass = [].__proto__ ? function(array, prototype) { + array.__proto__ = prototype; + } : function(array, prototype) { + for (var property in prototype) array[property] = prototype[property]; + }; + d3.map = function(object) { + var map = new d3_Map; + for (var key in object) map.set(key, object[key]); + return map; + }; + d3_class(d3_Map, { + has: function(key) { + return d3_map_prefix + key in this; + }, + get: function(key) { + return this[d3_map_prefix + key]; + }, + set: function(key, value) { + return this[d3_map_prefix + key] = value; + }, + remove: function(key) { + key = d3_map_prefix + key; + return key in this && delete this[key]; + }, + keys: function() { + var keys = []; + this.forEach(function(key) { + keys.push(key); + }); + return keys; + }, + values: function() { + var values = []; + this.forEach(function(key, value) { + values.push(value); + }); + return values; + }, + entries: function() { + var entries = []; + this.forEach(function(key, value) { + entries.push({ + key: key, + value: value + }); + }); + return entries; + }, + forEach: function(f) { + for (var key in this) { + if (key.charCodeAt(0) === d3_map_prefixCode) { + f.call(this, key.substring(1), this[key]); + } + } + } + }); + var d3_map_prefix = "\0", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); + d3.functor = d3_functor; + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + d3.ascending = function(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + }; + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.mean = function(array, f) { + var n = array.length, a, m = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; + } else { + while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; + } + return j ? m : undefined; + }; + d3.median = function(array, f) { + if (arguments.length > 1) array = array.map(f); + array = array.filter(d3_number); + return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && ((a = array[i]) == null || a != a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && ((a = array[i]) == null || a != a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n && ((a = c = array[i]) == null || a != a)) a = c = undefined; + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n && ((a = c = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + var random = d3.random.normal(); + return function() { + return Math.exp(µ + σ * random()); + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s / m; + }; + } + }; + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (!isNaN(a = +array[i])) s += a; + } else { + while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.transpose = function(matrix) { + return d3.zip.apply(d3, matrix); + }; + d3.zip = function() { + if (!(n = arguments.length)) return []; + for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { + for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { + zip[j] = arguments[j][i]; + } + } + return zips; + }; + d3.bisector = function(f) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (f.call(a, a[mid], mid) < x) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (x < f.call(a, a[mid], mid)) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + }; + var d3_bisector = d3.bisector(function(d) { + return d; + }); + d3.bisectLeft = d3_bisector.left; + d3.bisect = d3.bisectRight = d3_bisector.right; + d3.first = function(array, f) { + var i = 0, n = array.length, a = array[0], b; + if (arguments.length === 1) f = d3.ascending; + while (++i < n) { + if (f.call(array, a, b = array[i]) > 0) { + a = b; + } + } + return a; + }; + d3.last = function(array, f) { + var i = 0, n = array.length, a = array[0], b; + if (arguments.length === 1) f = d3.ascending; + while (++i < n) { + if (f.call(array, a, b = array[i]) <= 0) { + a = b; + } + } + return a; + }; + d3.nest = function() { + function map(array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, valuesByKey = new d3_Map, values, o = {}; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + valuesByKey.forEach(function(keyValue, values) { + o[keyValue] = map(values, depth); + }); + return o; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var a = [], sortKey = sortKeys[depth++], key; + for (key in map) { + a.push({ + key: key, + values: entries(map[key], depth) + }); + } + if (sortKey) a.sort(function(a, b) { + return sortKey(a.key, b.key); + }); + return a; + } + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + nest.map = function(array) { + return map(array, 0); + }; + nest.entries = function(array) { + return entries(map(array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.permute = function(array, indexes) { + var permutes = [], i = -1, n = indexes.length; + while (++i < n) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.merge = function(arrays) { + return Array.prototype.concat.apply([], arrays); + }; + d3.split = function(array, f) { + var arrays = [], values = [], value, i = -1, n = array.length; + if (arguments.length < 2) f = d3_splitter; + while (++i < n) { + if (f.call(values, value = array[i], i)) { + values = []; + } else { + if (!values.length) arrays.push(values); + values.push(value); + } + } + return arrays; + }; + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(Math.abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + d3.xhr = function(url, mime, callback) { + var req = new XMLHttpRequest; + if (arguments.length < 3) callback = mime, mime = null; else if (mime && req.overrideMimeType) req.overrideMimeType(mime); + req.open("GET", url, true); + if (mime) req.setRequestHeader("Accept", mime); + req.onreadystatechange = function() { + if (req.readyState === 4) { + var s = req.status; + callback(!s && req.response || s >= 200 && s < 300 || s === 304 ? req : null); + } + }; + req.send(null); + }; + d3.text = function(url, mime, callback) { + function ready(req) { + callback(req && req.responseText); + } + if (arguments.length < 3) { + callback = mime; + mime = null; + } + d3.xhr(url, mime, ready); + }; + d3.json = function(url, callback) { + d3.text(url, "application/json", function(text) { + callback(text ? JSON.parse(text) : null); + }); + }; + d3.html = function(url, callback) { + d3.text(url, "text/html", function(text) { + if (text != null) { + var range = document.createRange(); + range.selectNode(document.body); + text = range.createContextualFragment(text); + } + callback(text); + }); + }; + d3.xml = function(url, mime, callback) { + function ready(req) { + callback(req && req.responseXML); + } + if (arguments.length < 3) { + callback = mime; + mime = null; + } + d3.xhr(url, mime, ready); + }; + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0) { + prefix = name.substring(0, i); + name = name.substring(i + 1); + } + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3.dispatch = function() { + var dispatch = new d3_dispatch, i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i > 0) { + name = type.substring(i + 1); + type = type.substring(0, i); + } + return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + }; + d3.format = function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", sign = match[3] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, suffix = "", integer = false; + if (precision) precision = +precision.substring(1); + if (zfill) { + fill = "0"; + if (comma) width -= Math.floor((width - 1) / 4); + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + case "d": + integer = true; + precision = 0; + break; + case "s": + scale = -1; + type = "r"; + break; + } + if (type == "r" && !precision) type = "g"; + type = d3_format_types.get(type) || d3_format_typeDefault; + return function(value) { + if (integer && value % 1) return ""; + var negative = value < 0 && (value = -value) ? "-" : sign; + if (scale < 0) { + var prefix = d3.formatPrefix(value, precision); + value = prefix.scale(value); + suffix = prefix.symbol; + } else { + value *= scale; + } + value = type(value, precision); + if (zfill) { + var length = value.length + negative.length; + if (length < width) value = (new Array(width - length + 1)).join(fill) + value; + if (comma) value = d3_format_group(value); + value = negative + value; + } else { + if (comma) value = d3_format_group(value); + value = negative + value; + var length = value.length; + if (length < width) value = (new Array(width - length + 1)).join(fill) + value; + } + return value + suffix; + }; + }; + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/; + var d3_format_types = d3.map({ + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return d3.round(x, p = d3_format_precision(x, p)).toFixed(Math.max(0, Math.min(20, p))); + } + }); + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "μ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + var d3_ease_quad = d3_ease_poly(2), d3_ease_cubic = d3_ease_poly(3), d3_ease_default = function() { + return d3_ease_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_ease_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_ease_identity; + return d3_ease_clamp(m(t.apply(null, Array.prototype.slice.call(arguments, 1)))); + }; + d3.event = null; + d3.transform = function(string) { + var g = document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + var d3_transformDegrees = 180 / Math.PI, d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolate = function(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + }; + d3.interpolateNumber = function(a, b) { + b -= a; + return function(t) { + return a + b * t; + }; + }; + d3.interpolateRound = function(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + }; + d3.interpolateString = function(a, b) { + var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o; + d3_interpolate_number.lastIndex = 0; + for (i = 0; m = d3_interpolate_number.exec(b); ++i) { + if (m.index) s.push(b.substring(s0, s1 = m.index)); + q.push({ + i: s.length, + x: m[0] + }); + s.push(null); + s0 = d3_interpolate_number.lastIndex; + } + if (s0 < b.length) s.push(b.substring(s0)); + for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { + o = q[i]; + if (o.x == m[0]) { + if (o.i) { + if (s[o.i + 1] == null) { + s[o.i - 1] += o.x; + s.splice(o.i, 1); + for (j = i + 1; j < n; ++j) q[j].i--; + } else { + s[o.i - 1] += o.x + s[o.i + 1]; + s.splice(o.i, 2); + for (j = i + 1; j < n; ++j) q[j].i -= 2; + } + } else { + if (s[o.i + 1] == null) { + s[o.i] = o.x; + } else { + s[o.i] = o.x + s[o.i + 1]; + s.splice(o.i + 1, 1); + for (j = i + 1; j < n; ++j) q[j].i--; + } + } + q.splice(i, 1); + n--; + i--; + } else { + o.x = d3.interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); + } + } + while (i < n) { + o = q.pop(); + if (s[o.i + 1] == null) { + s[o.i] = o.x; + } else { + s[o.i] = o.x + s[o.i + 1]; + s.splice(o.i + 1, 1); + } + n--; + } + if (s.length === 1) { + return s[0] == null ? q[0].x : function() { + return b; + }; + } + return function(t) { + for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; + d3.interpolateTransform = function(a, b) { + var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; + if (ta[0] != tb[0] || ta[1] != tb[1]) { + s.push("translate(", null, ",", null, ")"); + q.push({ + i: 1, + x: d3.interpolateNumber(ta[0], tb[0]) + }, { + i: 3, + x: d3.interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } else { + s.push(""); + } + if (ra != rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(s.pop() + "rotate(", null, ")") - 2, + x: d3.interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(s.pop() + "rotate(" + rb + ")"); + } + if (wa != wb) { + q.push({ + i: s.push(s.pop() + "skewX(", null, ")") - 2, + x: d3.interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(s.pop() + "skewX(" + wb + ")"); + } + if (ka[0] != kb[0] || ka[1] != kb[1]) { + n = s.push(s.pop() + "scale(", null, ",", null, ")"); + q.push({ + i: n - 4, + x: d3.interpolateNumber(ka[0], kb[0]) + }, { + i: n - 2, + x: d3.interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] != 1 || kb[1] != 1) { + s.push(s.pop() + "scale(" + kb + ")"); + } + n = q.length; + return function(t) { + var i = -1, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; + d3.interpolateRgb = function(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + }; + d3.interpolateHsl = function(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var h0 = a.h, s0 = a.s, l0 = a.l, h1 = b.h - h0, s1 = b.s - s0, l1 = b.l - l0; + if (h1 > 180) h1 -= 360; else if (h1 < -180) h1 += 360; + return function(t) { + return d3_hsl_rgb(h0 + h1 * t, s0 + s1 * t, l0 + l1 * t) + ""; + }; + }; + d3.interpolateLab = function(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + }; + d3.interpolateHcl = function(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + }; + d3.interpolateArray = function(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3.interpolate(a[i], b[i])); + for (; i < na; ++i) c[i] = a[i]; + for (; i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + }; + d3.interpolateObject = function(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolateByName(k)(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + }; + var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; + d3.interpolators = [ d3.interpolateObject, function(a, b) { + return b instanceof Array && d3.interpolateArray(a, b); + }, function(a, b) { + return (typeof a === "string" || typeof b === "string") && d3.interpolateString(a + "", b + ""); + }, function(a, b) { + return (typeof b === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Color) && d3.interpolateRgb(a, b); + }, function(a, b) { + return !isNaN(a = +a) && !isNaN(b = +b) && d3.interpolateNumber(a, b); + } ]; + d3_Color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.rgb = function(r, g, b) { + return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b); + }; + var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color; + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return d3_rgb(Math.min(255, Math.floor(r / k)), Math.min(255, Math.floor(g / k)), Math.min(255, Math.floor(b / k))); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_rgb(Math.floor(k * this.r), Math.floor(k * this.g), Math.floor(k * this.b)); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + var d3_rgb_names = d3.map({ + aliceblue: "#f0f8ff", + antiquewhite: "#faebd7", + aqua: "#00ffff", + aquamarine: "#7fffd4", + azure: "#f0ffff", + beige: "#f5f5dc", + bisque: "#ffe4c4", + black: "#000000", + blanchedalmond: "#ffebcd", + blue: "#0000ff", + blueviolet: "#8a2be2", + brown: "#a52a2a", + burlywood: "#deb887", + cadetblue: "#5f9ea0", + chartreuse: "#7fff00", + chocolate: "#d2691e", + coral: "#ff7f50", + cornflowerblue: "#6495ed", + cornsilk: "#fff8dc", + crimson: "#dc143c", + cyan: "#00ffff", + darkblue: "#00008b", + darkcyan: "#008b8b", + darkgoldenrod: "#b8860b", + darkgray: "#a9a9a9", + darkgreen: "#006400", + darkgrey: "#a9a9a9", + darkkhaki: "#bdb76b", + darkmagenta: "#8b008b", + darkolivegreen: "#556b2f", + darkorange: "#ff8c00", + darkorchid: "#9932cc", + darkred: "#8b0000", + darksalmon: "#e9967a", + darkseagreen: "#8fbc8f", + darkslateblue: "#483d8b", + darkslategray: "#2f4f4f", + darkslategrey: "#2f4f4f", + darkturquoise: "#00ced1", + darkviolet: "#9400d3", + deeppink: "#ff1493", + deepskyblue: "#00bfff", + dimgray: "#696969", + dimgrey: "#696969", + dodgerblue: "#1e90ff", + firebrick: "#b22222", + floralwhite: "#fffaf0", + forestgreen: "#228b22", + fuchsia: "#ff00ff", + gainsboro: "#dcdcdc", + ghostwhite: "#f8f8ff", + gold: "#ffd700", + goldenrod: "#daa520", + gray: "#808080", + green: "#008000", + greenyellow: "#adff2f", + grey: "#808080", + honeydew: "#f0fff0", + hotpink: "#ff69b4", + indianred: "#cd5c5c", + indigo: "#4b0082", + ivory: "#fffff0", + khaki: "#f0e68c", + lavender: "#e6e6fa", + lavenderblush: "#fff0f5", + lawngreen: "#7cfc00", + lemonchiffon: "#fffacd", + lightblue: "#add8e6", + lightcoral: "#f08080", + lightcyan: "#e0ffff", + lightgoldenrodyellow: "#fafad2", + lightgray: "#d3d3d3", + lightgreen: "#90ee90", + lightgrey: "#d3d3d3", + lightpink: "#ffb6c1", + lightsalmon: "#ffa07a", + lightseagreen: "#20b2aa", + lightskyblue: "#87cefa", + lightslategray: "#778899", + lightslategrey: "#778899", + lightsteelblue: "#b0c4de", + lightyellow: "#ffffe0", + lime: "#00ff00", + limegreen: "#32cd32", + linen: "#faf0e6", + magenta: "#ff00ff", + maroon: "#800000", + mediumaquamarine: "#66cdaa", + mediumblue: "#0000cd", + mediumorchid: "#ba55d3", + mediumpurple: "#9370db", + mediumseagreen: "#3cb371", + mediumslateblue: "#7b68ee", + mediumspringgreen: "#00fa9a", + mediumturquoise: "#48d1cc", + mediumvioletred: "#c71585", + midnightblue: "#191970", + mintcream: "#f5fffa", + mistyrose: "#ffe4e1", + moccasin: "#ffe4b5", + navajowhite: "#ffdead", + navy: "#000080", + oldlace: "#fdf5e6", + olive: "#808000", + olivedrab: "#6b8e23", + orange: "#ffa500", + orangered: "#ff4500", + orchid: "#da70d6", + palegoldenrod: "#eee8aa", + palegreen: "#98fb98", + paleturquoise: "#afeeee", + palevioletred: "#db7093", + papayawhip: "#ffefd5", + peachpuff: "#ffdab9", + peru: "#cd853f", + pink: "#ffc0cb", + plum: "#dda0dd", + powderblue: "#b0e0e6", + purple: "#800080", + red: "#ff0000", + rosybrown: "#bc8f8f", + royalblue: "#4169e1", + saddlebrown: "#8b4513", + salmon: "#fa8072", + sandybrown: "#f4a460", + seagreen: "#2e8b57", + seashell: "#fff5ee", + sienna: "#a0522d", + silver: "#c0c0c0", + skyblue: "#87ceeb", + slateblue: "#6a5acd", + slategray: "#708090", + slategrey: "#708090", + snow: "#fffafa", + springgreen: "#00ff7f", + steelblue: "#4682b4", + tan: "#d2b48c", + teal: "#008080", + thistle: "#d8bfd8", + tomato: "#ff6347", + turquoise: "#40e0d0", + violet: "#ee82ee", + wheat: "#f5deb3", + white: "#ffffff", + whitesmoke: "#f5f5f5", + yellow: "#ffff00", + yellowgreen: "#9acd32" + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgb_parse(value, d3_rgb, d3_hsl_rgb)); + }); + d3.hsl = function(h, s, l) { + return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l); + }; + var d3_hslPrototype = d3_Hsl.prototype = new d3_Color; + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + d3.hcl = function(h, c, l) { + return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l); + }; + var d3_hclPrototype = d3_Hcl.prototype = new d3_Color; + d3_hclPrototype.brighter = function(k) { + return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + d3.lab = function(l, a, b) { + return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b); + }; + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_Lab.prototype = new d3_Color; + d3_labPrototype.brighter = function(k) { + return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectRoot = document.documentElement, d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector, d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = function(s, n) { + return Sizzle.uniqueSort(Sizzle(s, n)); + }; + d3_selectMatches = Sizzle.matchesSelector; + } + var d3_selectionPrototype = []; + d3.selection = function() { + return d3_selectionRoot; + }; + d3.selection.prototype = d3_selectionPrototype; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + if (typeof selector !== "function") selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = name.trim().split(/^|\s+/g)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.className; + if (value.baseVal != null) value = value.baseVal; + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) return window.getComputedStyle(this.node(), null).getPropertyValue(name); + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + d3_selectionPrototype.text = function(value) { + return arguments.length < 1 ? this.node().textContent : this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }); + }; + d3_selectionPrototype.html = function(value) { + return arguments.length < 1 ? this.node().innerHTML : this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }); + }; + d3_selectionPrototype.append = function(name) { + function append() { + return this.appendChild(document.createElementNS(this.namespaceURI, name)); + } + function appendNS() { + return this.appendChild(document.createElementNS(name.space, name.local)); + } + name = d3.ns.qualify(name); + return this.select(name.local ? appendNS : append); + }; + d3_selectionPrototype.insert = function(name, before) { + function insert() { + return this.insertBefore(document.createElementNS(this.namespaceURI, name), d3_select(before, this)); + } + function insertNS() { + return this.insertBefore(document.createElementNS(name.space, name.local), d3_select(before, this)); + } + name = d3.ns.qualify(name); + return this.select(name.local ? insertNS : insert); + }; + d3_selectionPrototype.remove = function() { + return this.each(function() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + }); + }; + d3_selectionPrototype.data = function(value, key) { + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), n1 = Math.max(n, m), updateNodes = [], enterNodes = [], exitNodes = [], node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map, keyValues = [], keyValue, j = groupData.length; + for (i = -1; ++i < n; ) { + keyValue = key.call(node = group[i], node.__data__, i); + if (nodeByKeyValue.has(keyValue)) { + exitNodes[j++] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues.push(keyValue); + } + for (i = -1; ++i < m; ) { + keyValue = key.call(groupData, nodeData = groupData[i], i); + if (nodeByKeyValue.has(keyValue)) { + updateNodes[i] = node = nodeByKeyValue.get(keyValue); + node.__data__ = nodeData; + enterNodes[i] = exitNodes[i] = null; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + updateNodes[i] = exitNodes[i] = null; + } + nodeByKeyValue.remove(keyValue); + } + for (i = -1; ++i < n; ) { + if (nodeByKeyValue.has(keyValues[i])) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + enterNodes[i] = exitNodes[i] = null; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + updateNodes[i] = exitNodes[i] = null; + } + } + for (; i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + updateNodes[i] = exitNodes[i] = null; + } + for (; i < n1; ++i) { + exitNodes[i] = group[i]; + enterNodes[i] = updateNodes[i] = null; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + d3_selectionPrototype.datum = d3_selectionPrototype.map = function(value) { + return arguments.length < 1 ? this.property("__data__") : this.property("__data__", value); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + d3_selectionPrototype.call = function(callback) { + callback.apply(this, (arguments[0] = this, arguments)); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function(callback) { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.transition = function() { + var subgroups = [], subgroup, node; + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + subgroup.push((node = group[i]) ? { + node: node, + delay: d3_transitionDelay, + duration: d3_transitionDuration + } : null); + } + } + return d3_transition(subgroups, d3_transitionId || ++d3_transitionNextId, Date.now()); + }; + var d3_selectionRoot = d3_selection([ [ document ] ]); + d3_selectionRoot[0].parentNode = d3_selectRoot; + d3.select = function(selector) { + return typeof selector === "string" ? d3_selectionRoot.select(selector) : d3_selection([ [ selector ] ]); + }; + d3.selectAll = function(selector) { + return typeof selector === "string" ? d3_selectionRoot.selectAll(selector) : d3_selection([ d3_array(selector) ]); + }; + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.insert = d3_selectionPrototype.insert; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + var d3_transitionPrototype = [], d3_transitionNextId = 0, d3_transitionId = 0, d3_transitionDefaultDelay = 0, d3_transitionDefaultDuration = 250, d3_transitionDefaultEase = d3.ease("cubic-in-out"), d3_transitionDelay = d3_transitionDefaultDelay, d3_transitionDuration = d3_transitionDefaultDuration, d3_transitionEase = d3_transitionDefaultEase; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3.transition = function(selection) { + return arguments.length ? d3_transitionId ? selection.transition() : selection : d3_selectionRoot.transition(); + }; + d3.transition.prototype = d3_transitionPrototype; + d3_transitionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, node; + if (typeof selector !== "function") selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node.node, node.node.__data__, i))) { + if ("__data__" in node.node) subnode.__data__ = node.node.__data__; + subgroup.push({ + node: subnode, + delay: node.delay, + duration: node.duration + }); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, this.id, this.time).ease(this.ease()); + }; + d3_transitionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, subnodes, node; + if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subnodes = selector.call(node.node, node.node.__data__, i); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + subgroup.push({ + node: subnodes[k], + delay: node.delay, + duration: node.duration + }); + } + } + } + } + return d3_transition(subgroups, this.id, this.time).ease(this.ease()); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node.node, node.node.__data__, i)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.id, this.time).ease(this.ease()); + }; + d3_transitionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + for (value in name) this.attrTween(value, d3_tweenByName(name[value], value)); + return this; + } + return this.attrTween(name, d3_tweenByName(value, name)); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f === d3_tweenRemove ? (this.removeAttribute(name), null) : f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f === d3_tweenRemove ? (this.removeAttributeNS(name.space, name.local), null) : f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + var name = d3.ns.qualify(nameNS); + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.styleTween(priority, d3_tweenByName(name[priority], priority), value); + return this; + } + priority = ""; + } + return this.styleTween(name, d3_tweenByName(value, name), priority); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + return this.tween("style." + name, function(d, i) { + var f = tween.call(this, d, i, window.getComputedStyle(this, null).getPropertyValue(name)); + return f === d3_tweenRemove ? (this.style.removeProperty(name), null) : f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + }); + }; + d3_transitionPrototype.text = function(value) { + return this.tween("text", function(d, i) { + this.textContent = typeof value === "function" ? value.call(this, d, i) : value; + }); + }; + d3_transitionPrototype.remove = function() { + return this.each("end.transition", function() { + var p; + if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.delay = function(value) { + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.delay = value.call(node = node.node, node.__data__, i, j) | 0; + } : (value = value | 0, function(node) { + node.delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.duration = Math.max(1, value.call(node = node.node, node.__data__, i, j) | 0); + } : (value = Math.max(1, value | 0), function(node) { + node.duration = value; + })); + }; + d3_transitionPrototype.transition = function() { + return this.select(d3_this); + }; + d3.tween = function(b, interpolate) { + function tweenFunction(d, i, a) { + var v = b.call(this, d, i); + return v == null ? a != "" && d3_tweenRemove : a != v && interpolate(a, v + ""); + } + function tweenString(d, i, a) { + return a != b && interpolate(a, b); + } + return typeof b === "function" ? tweenFunction : b == null ? d3_tweenNull : (b += "", tweenString); + }; + var d3_tweenRemove = {}; + var d3_timer_id = 0, d3_timer_byId = {}, d3_timer_queue = null, d3_timer_interval, d3_timer_timeout; + d3.timer = function(callback, delay, then) { + if (arguments.length < 3) { + if (arguments.length < 2) delay = 0; else if (!isFinite(delay)) return; + then = Date.now(); + } + var timer = d3_timer_byId[callback.id]; + if (timer && timer.callback === callback) { + timer.then = then; + timer.delay = delay; + } else d3_timer_byId[callback.id = ++d3_timer_id] = d3_timer_queue = { + callback: callback, + then: then, + delay: delay, + next: d3_timer_queue + }; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + }; + d3.timer.flush = function() { + var elapsed, now = Date.now(), t1 = d3_timer_queue; + while (t1) { + elapsed = now - t1.then; + if (!t1.delay) t1.flush = t1.callback(elapsed); + t1 = t1.next; + } + d3_timer_flush(); + }; + var d3_timer_frame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { + setTimeout(callback, 17); + }; + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = /WebKit/.test(navigator.userAgent) ? -1 : 0; + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + d3.scale = {}; + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3.interpolate, false); + }; + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear(), d3_scale_logp); + }; + var d3_scale_logFormat = d3.format(".0e"); + d3_scale_logp.pow = function(x) { + return Math.pow(10, x); + }; + d3_scale_logn.pow = function(x) { + return -Math.pow(10, -x); + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1); + }; + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf" ]; + var d3_category20 = [ "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5" ]; + var d3_category20b = [ "#393b79", "#5254a3", "#6b6ecf", "#9c9ede", "#637939", "#8ca252", "#b5cf6b", "#cedb9c", "#8c6d31", "#bd9e39", "#e7ba52", "#e7cb94", "#843c39", "#ad494a", "#d6616b", "#e7969c", "#7b4173", "#a55194", "#ce6dbd", "#de9ed6" ]; + var d3_category20c = [ "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d", "#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476", "#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc", "#dadaeb", "#636363", "#969696", "#bdbdbd", "#d9d9d9" ]; + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + d3.svg = {}; + d3.svg.arc = function() { + function arc() { + var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, a0 = a1, a1 = da), a1 - a0), df = da < Math.PI ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1); + return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z"; + } + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcOffset = -Math.PI / 2, d3_svg_arcMax = 2 * Math.PI - 1e-6; + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > Math.PI) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + var source = d3_svg_chordSource, target = d3_svg_chordTarget, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + d3.svg.diagonal = function() { + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + var source = d3_svg_chordSource, target = d3_svg_chordTarget, projection = d3_svg_diagonalProjection; + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + d3.svg.mouse = d3.mouse; + d3.svg.touches = d3.touches; + d3.svg.symbol = function() { + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * Math.PI / 180); + d3.svg.axis = function() { + function axis(g) { + g.each(function() { + var g = d3.select(this); + var ticks = tickValues == null ? scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain() : tickValues, tickFormat = tickFormat_ == null ? scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments_) : String : tickFormat_; + var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide), subtick = g.selectAll(".minor").data(subticks, String), subtickEnter = subtick.enter().insert("line", "g").attr("class", "tick minor").style("opacity", 1e-6), subtickExit = d3.transition(subtick.exit()).style("opacity", 1e-6).remove(), subtickUpdate = d3.transition(subtick).style("opacity", 1); + var tick = g.selectAll("g").data(ticks, String), tickEnter = tick.enter().insert("g", "path").style("opacity", 1e-6), tickExit = d3.transition(tick.exit()).style("opacity", 1e-6).remove(), tickUpdate = d3.transition(tick).style("opacity", 1), tickTransform; + var range = d3_scaleRange(scale), path = g.selectAll(".domain").data([ 0 ]), pathEnter = path.enter().append("path").attr("class", "domain"), pathUpdate = d3.transition(path); + var scale1 = scale.copy(), scale0 = this.__chart__ || scale1; + this.__chart__ = scale1; + tickEnter.append("line").attr("class", "tick"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); + switch (orient) { + case "bottom": + { + tickTransform = d3_svg_axisX; + subtickEnter.attr("y2", tickMinorSize); + subtickUpdate.attr("x2", 0).attr("y2", tickMinorSize); + lineEnter.attr("y2", tickMajorSize); + textEnter.attr("y", Math.max(tickMajorSize, 0) + tickPadding); + lineUpdate.attr("x2", 0).attr("y2", tickMajorSize); + textUpdate.attr("x", 0).attr("y", Math.max(tickMajorSize, 0) + tickPadding); + text.attr("dy", ".71em").attr("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize); + break; + } + case "top": + { + tickTransform = d3_svg_axisX; + subtickEnter.attr("y2", -tickMinorSize); + subtickUpdate.attr("x2", 0).attr("y2", -tickMinorSize); + lineEnter.attr("y2", -tickMajorSize); + textEnter.attr("y", -(Math.max(tickMajorSize, 0) + tickPadding)); + lineUpdate.attr("x2", 0).attr("y2", -tickMajorSize); + textUpdate.attr("x", 0).attr("y", -(Math.max(tickMajorSize, 0) + tickPadding)); + text.attr("dy", "0em").attr("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize); + break; + } + case "left": + { + tickTransform = d3_svg_axisY; + subtickEnter.attr("x2", -tickMinorSize); + subtickUpdate.attr("x2", -tickMinorSize).attr("y2", 0); + lineEnter.attr("x2", -tickMajorSize); + textEnter.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)); + lineUpdate.attr("x2", -tickMajorSize).attr("y2", 0); + textUpdate.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)).attr("y", 0); + text.attr("dy", ".32em").attr("text-anchor", "end"); + pathUpdate.attr("d", "M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize); + break; + } + case "right": + { + tickTransform = d3_svg_axisY; + subtickEnter.attr("x2", tickMinorSize); + subtickUpdate.attr("x2", tickMinorSize).attr("y2", 0); + lineEnter.attr("x2", tickMajorSize); + textEnter.attr("x", Math.max(tickMajorSize, 0) + tickPadding); + lineUpdate.attr("x2", tickMajorSize).attr("y2", 0); + textUpdate.attr("x", Math.max(tickMajorSize, 0) + tickPadding).attr("y", 0); + text.attr("dy", ".32em").attr("text-anchor", "start"); + pathUpdate.attr("d", "M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize); + break; + } + } + if (scale.ticks) { + tickEnter.call(tickTransform, scale0); + tickUpdate.call(tickTransform, scale1); + tickExit.call(tickTransform, scale1); + subtickEnter.call(tickTransform, scale0); + subtickUpdate.call(tickTransform, scale1); + subtickExit.call(tickTransform, scale1); + } else { + var dx = scale1.rangeBand() / 2, x = function(d) { + return scale1(d) + dx; + }; + tickEnter.call(tickTransform, x); + tickUpdate.call(tickTransform, x); + } + }); + } + var scale = d3.scale.linear(), orient = "bottom", tickMajorSize = 6, tickMinorSize = 6, tickEndSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_, tickSubdivide = 0; + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = arguments; + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x, y, z) { + if (!arguments.length) return tickMajorSize; + var n = arguments.length - 1; + tickMajorSize = +x; + tickMinorSize = n > 1 ? +y : tickMajorSize; + tickEndSize = n > 0 ? +arguments[n] : tickMajorSize; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function(x) { + if (!arguments.length) return tickSubdivide; + tickSubdivide = +x; + return axis; + }; + return axis; + }; + d3.svg.brush = function() { + function brush(g) { + g.each(function() { + var g = d3.select(this), bg = g.selectAll(".background").data([ 0 ]), fg = g.selectAll(".extent").data([ 0 ]), tz = g.selectAll(".resize").data(resizes, String), e; + g.style("pointer-events", "all").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + bg.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + fg.enter().append("rect").attr("class", "extent").style("cursor", "move"); + tz.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + tz.style("display", brush.empty() ? "none" : null); + tz.exit().remove(); + if (x) { + e = d3_scaleRange(x); + bg.attr("x", e[0]).attr("width", e[1] - e[0]); + redrawX(g); + } + if (y) { + e = d3_scaleRange(y); + bg.attr("y", e[0]).attr("height", e[1] - e[0]); + redrawY(g); + } + redraw(g); + }); + } + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + extent[+/e$/.test(d)][0] + "," + extent[+/^s/.test(d)][1] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", extent[0][0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", extent[1][0] - extent[0][0]); + } + function redrawY(g) { + g.select(".extent").attr("y", extent[0][1]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", extent[1][1] - extent[0][1]); + } + function brushstart() { + function mouse() { + var touches = d3.event.changedTouches; + return touches ? d3.touches(target, touches)[0] : d3.mouse(target); + } + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= extent[1][0]; + origin[1] -= extent[1][1]; + dragging = 2; + } + d3_eventCancel(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += extent[1][0]; + origin[1] += extent[1][1]; + dragging = 0; + d3_eventCancel(); + } + } + function brushmove() { + var point = mouse(), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2 ]; + origin[0] = extent[+(point[0] < center[0])][0]; + origin[1] = extent[+(point[1] < center[1])][1]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], size = extent[1][i] - extent[0][i], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = Math.max(r0, Math.min(r1, point[i])); + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0][i] !== min || extent[1][i] !== max) { + extentDomain = null; + extent[0][i] = min; + extent[1][i] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + event_({ + type: "brushend" + }); + d3_eventCancel(); + } + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), center, origin = mouse(), offset; + var w = d3.select(window).on("mousemove.brush", brushmove).on("mouseup.brush", brushend).on("touchmove.brush", brushmove).on("touchend.brush", brushend).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (dragging) { + origin[0] = extent[0][0] - origin[0]; + origin[1] = extent[0][1] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ extent[1 - ex][0] - origin[0], extent[1 - ey][1] - origin[1] ]; + origin[0] = extent[ex][0]; + origin[1] = extent[ey][1]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + d3_eventCancel(); + } + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, resizes = d3_svg_brushResizes[0], extent = [ [ 0, 0 ], [ 0, 0 ] ], extentDomain; + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + z = extentDomain || extent; + if (x) { + x0 = z[0][0], x1 = z[1][0]; + if (!extentDomain) { + x0 = extent[0][0], x1 = extent[1][0]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + y0 = z[0][1], y1 = z[1][1]; + if (!extentDomain) { + y0 = extent[0][1], y1 = extent[1][1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + extentDomain = [ [ 0, 0 ], [ 0, 0 ] ]; + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + extentDomain[0][0] = x0, extentDomain[1][0] = x1; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + extent[0][0] = x0 | 0, extent[1][0] = x1 | 0; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + extentDomain[0][1] = y0, extentDomain[1][1] = y1; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + extent[0][1] = y0 | 0, extent[1][1] = y1 | 0; + } + return brush; + }; + brush.clear = function() { + extentDomain = null; + extent[0][0] = extent[0][1] = extent[1][0] = extent[1][1] = 0; + return brush; + }; + brush.empty = function() { + return x && extent[0][0] === extent[1][0] || y && extent[0][1] === extent[1][1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + d3.behavior = {}; + d3.behavior.drag = function() { + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown); + } + function mousedown() { + function point() { + var p = target.parentNode; + return touchId ? d3.touches(p).filter(function(p) { + return p.identifier === touchId; + })[0] : d3.mouse(p); + } + function dragmove() { + if (!target.parentNode) return dragend(); + var p = point(), dx = p[0] - origin_[0], dy = p[1] - origin_[1]; + moved |= dx | dy; + origin_ = p; + d3_eventCancel(); + event_({ + type: "drag", + x: p[0] + offset[0], + y: p[1] + offset[1], + dx: dx, + dy: dy + }); + } + function dragend() { + event_({ + type: "dragend" + }); + if (moved) { + d3_eventCancel(); + if (d3.event.target === eventTarget) w.on("click.drag", click, true); + } + w.on(touchId ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId ? "touchend.drag-" + touchId : "mouseup.drag", null); + } + function click() { + d3_eventCancel(); + w.on("click.drag", null); + } + var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches && d3.event.changedTouches[0].identifier, offset, origin_ = point(), moved = 0; + var w = d3.select(window).on(touchId ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove).on(touchId ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true); + if (origin) { + offset = origin.apply(target, arguments); + offset = [ offset.x - origin_[0], offset.y - origin_[1] ]; + } else { + offset = [ 0, 0 ]; + } + if (!touchId) d3_eventCancel(); + event_({ + type: "dragstart" + }); + } + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null; + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + d3.behavior.zoom = function() { + function zoom() { + this.on("mousedown.zoom", mousedown).on("mousewheel.zoom", mousewheel).on("mousemove.zoom", mousemove).on("DOMMouseScroll.zoom", mousewheel).on("dblclick.zoom", dblclick).on("touchstart.zoom", touchstart).on("touchmove.zoom", touchmove).on("touchend.zoom", touchstart); + } + function location(p) { + return [ (p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale ]; + } + function point(l) { + return [ l[0] * scale + translate[0], l[1] * scale + translate[1] ]; + } + function scaleTo(s) { + scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + translate[0] += p[0] - l[0]; + translate[1] += p[1] - l[1]; + } + function dispatch(event) { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - translate[0]) / scale; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - translate[1]) / scale; + }).map(y0.invert)); + d3.event.preventDefault(); + event({ + type: "zoom", + scale: scale, + translate: translate + }); + } + function mousedown() { + function mousemove() { + moved = 1; + translateTo(d3.mouse(target), l); + dispatch(event_); + } + function mouseup() { + if (moved) d3_eventCancel(); + w.on("mousemove.zoom", null).on("mouseup.zoom", null); + if (moved && d3.event.target === eventTarget) w.on("click.zoom", click, true); + } + function click() { + d3_eventCancel(); + w.on("click.zoom", null); + } + var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, moved = 0, w = d3.select(window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), l = location(d3.mouse(target)); + window.focus(); + d3_eventCancel(); + } + function mousewheel() { + if (!translate0) translate0 = location(d3.mouse(this)); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale); + translateTo(d3.mouse(this), translate0); + dispatch(event.of(this, arguments)); + } + function mousemove() { + translate0 = null; + } + function dblclick() { + var p = d3.mouse(this), l = location(p); + scaleTo(d3.event.shiftKey ? scale / 2 : scale * 2); + translateTo(p, l); + dispatch(event.of(this, arguments)); + } + function touchstart() { + var touches = d3.touches(this), now = Date.now(); + scale0 = scale; + translate0 = {}; + touches.forEach(function(t) { + translate0[t.identifier] = location(t); + }); + d3_eventCancel(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0], l = location(touches[0]); + scaleTo(scale * 2); + translateTo(p, l); + dispatch(event.of(this, arguments)); + } + touchtime = now; + } + } + function touchmove() { + var touches = d3.touches(this), p0 = touches[0], l0 = translate0[p0.identifier]; + if (p1 = touches[1]) { + var p1, l1 = translate0[p1.identifier]; + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(d3.event.scale * scale0); + } + translateTo(p0, l0); + touchtime = null; + dispatch(event.of(this, arguments)); + } + var translate = [ 0, 0 ], translate0, scale = 1, scale0, scaleExtent = d3_behavior_zoomInfinity, event = d3_eventDispatch(zoom, "zoom"), x0, x1, y0, y1, touchtime; + zoom.translate = function(x) { + if (!arguments.length) return translate; + translate = x.map(Number); + return zoom; + }; + zoom.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + return zoom; + }; + zoom.scaleExtent = function(x) { + if (!arguments.length) return scaleExtent; + scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number); + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + return zoom; + }; + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomDiv, d3_behavior_zoomInfinity = [ 0, Infinity ]; + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + d3.layout.chord = function() { + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (2 * Math.PI - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: (x - x0) / k + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + function repulse(node) { + return function(quad, x1, y1, x2, y2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dn = 1 / Math.sqrt(dx * dx + dy * dy); + if ((x2 - x1) * dn < theta) { + var k = quad.charge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + return true; + } + if (quad.point && isFinite(dn)) { + var k = quad.pointCharge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + function dragmove(d) { + d.px = d3.event.x; + d.py = d3.event.y; + force.resume(); + } + var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, gravity = .1, theta = .8, interval, nodes = [], links = [], distances, strengths, charges; + force.tick = function() { + if ((alpha *= .99) < .005) { + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = d3_functor(x); + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = d3_functor(x); + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return theta; + theta = x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + if (alpha) { + if (x > 0) alpha = x; else alpha = 0; + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + d3.timer(force.tick); + } + return force; + }; + force.start = function() { + function position(dimension, size) { + var neighbors = neighbor(i), j = -1, m = neighbors.length, x; + while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x; + return Math.random() * size; + } + function neighbor() { + if (!neighbors) { + neighbors = []; + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + return neighbors[i]; + } + var i, j, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + distances = []; + strengths = []; + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + distances[i] = linkDistance.call(this, o, i); + strengths[i] = linkStrength.call(this, o, i); + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + charges = []; + if (typeof charge === "function") { + for (i = 0; i < n; ++i) { + charges[i] = +charge.call(this, nodes[i], i); + } + } else { + for (i = 0; i < n; ++i) { + charges[i] = charge; + } + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart", d3_layout_forceDragstart).on("drag", dragmove).on("dragend", d3_layout_forceDragend); + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + return d3.rebind(force, event, "on"); + }; + d3.layout.partition = function() { + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + function pie(data, i) { + var values = data.map(function(d, i) { + return +value.call(pie, d, i); + }); + var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle); + var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - startAngle) / d3.sum(values); + var index = d3.range(data.length); + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + var arcs = []; + index.forEach(function(i) { + var d; + arcs[i] = { + data: data[i], + value: d = values[i], + startAngle: a, + endAngle: a += d * k + }; + }); + return arcs; + } + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = 2 * Math.PI; + pie.value = function(x) { + if (!arguments.length) return value; + value = x; + return pie; + }; + pie.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return pie; + }; + pie.startAngle = function(x) { + if (!arguments.length) return startAngle; + startAngle = x; + return pie; + }; + pie.endAngle = function(x) { + if (!arguments.length) return endAngle; + endAngle = x; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + function stack(data, index) { + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d, i) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var n = series.length, m = series[0].length, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, max = 0, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + d3.layout.histogram = function() { + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + d3.layout.hierarchy = function() { + function recurse(data, depth, nodes) { + var childs = children.call(hierarchy, data, depth), node = d3_layout_hierarchyInline ? data : { + data: data + }; + node.depth = depth; + nodes.push(node); + if (childs && (n = childs.length)) { + var i = -1, n, c = node.children = [], v = 0, j = depth + 1, d; + while (++i < n) { + d = recurse(childs[i], j, nodes); + d.parent = node; + c.push(d); + v += d.value; + } + if (sort) c.sort(sort); + if (value) node.value = v; + } else if (value) { + node.value = +value.call(hierarchy, data, depth) || 0; + } + return node; + } + function revalue(node, depth) { + var children = node.children, v = 0; + if (children && (n = children.length)) { + var i = -1, n, j = depth + 1; + while (++i < n) v += revalue(children[i], j); + } else if (value) { + v = +value.call(hierarchy, d3_layout_hierarchyInline ? node : node.data, depth) || 0; + } + if (value) node.value = v; + return v; + } + function hierarchy(d) { + var nodes = []; + recurse(d, 0, nodes); + return nodes; + } + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + revalue(root, 0); + return root; + }; + return hierarchy; + }; + var d3_layout_hierarchyInline = false; + d3.layout.pack = function() { + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0]; + root.x = 0; + root.y = 0; + d3_layout_treeVisitAfter(root, function(d) { + d.r = Math.sqrt(d.value); + }); + d3_layout_treeVisitAfter(root, d3_layout_packSiblings); + var w = size[0], h = size[1], k = Math.max(2 * root.r / w, 2 * root.r / h); + if (padding > 0) { + var dr = padding * k / 2; + d3_layout_treeVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_treeVisitAfter(root, d3_layout_packSiblings); + d3_layout_treeVisitAfter(root, function(d) { + d.r -= dr; + }); + k = Math.max(2 * root.r / w, 2 * root.r / h); + } + d3_layout_packTransform(root, w / 2, h / 2, 1 / k); + return nodes; + } + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ]; + pack.size = function(x) { + if (!arguments.length) return size; + size = x; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + d3.layout.cluster = function() { + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0, kx, ky; + d3_layout_treeVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_treeVisitAfter(root, function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ]; + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return size; + size = x; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + d3.layout.tree = function() { + function tree(d, i) { + function firstWalk(node, previousSibling) { + var children = node.children, layout = node._tree; + if (children && (n = children.length)) { + var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1; + while (++i < n) { + child = children[i]; + firstWalk(child, previousChild); + ancestor = apportion(child, previousChild, ancestor); + previousChild = child; + } + d3_layout_treeShift(node); + var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim); + if (previousSibling) { + layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); + layout.mod = layout.prelim - midpoint; + } else { + layout.prelim = midpoint; + } + } else { + if (previousSibling) { + layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); + } + } + } + function secondWalk(node, x) { + node.x = node._tree.prelim + x; + var children = node.children; + if (children && (n = children.length)) { + var i = -1, n; + x += node._tree.mod; + while (++i < n) { + secondWalk(children[i], x); + } + } + } + function apportion(node, previousSibling, ancestor) { + if (previousSibling) { + var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop._tree.ancestor = node; + shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift); + sip += shift; + sop += shift; + } + sim += vim._tree.mod; + sip += vip._tree.mod; + som += vom._tree.mod; + sop += vop._tree.mod; + } + if (vim && !d3_layout_treeRight(vop)) { + vop._tree.thread = vim; + vop._tree.mod += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom._tree.thread = vip; + vom._tree.mod += sip - som; + ancestor = node; + } + } + return ancestor; + } + var nodes = hierarchy.call(this, d, i), root = nodes[0]; + d3_layout_treeVisitAfter(root, function(node, previousSibling) { + node._tree = { + ancestor: node, + prelim: 0, + mod: 0, + change: 0, + shift: 0, + number: previousSibling ? previousSibling._tree.number + 1 : 0 + }; + }); + firstWalk(root); + secondWalk(root, -root._tree.prelim); + var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1; + d3_layout_treeVisitAfter(root, function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = node.depth / y1 * size[1]; + delete node._tree; + }); + return nodes; + } + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ]; + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return size; + size = x; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + d3.layout.treemap = function() { + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if ((score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = 0; + root.y = 0; + root.dx = size[0]; + root.dy = size[1]; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, ratio = .5 * (1 + Math.sqrt(5)); + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + if (!arguments.length) return padding; + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + d3.csv = d3_dsv(",", "text/csv"); + d3.tsv = d3_dsv(" ", "text/tab-separated-values"); + d3.geo = {}; + var d3_geo_radians = Math.PI / 180; + d3.geo.azimuthal = function() { + function azimuthal(coordinates) { + var x1 = coordinates[0] * d3_geo_radians - x0, y1 = coordinates[1] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1), cy1 = Math.cos(y1), sy1 = Math.sin(y1), cc = mode !== "orthographic" ? sy0 * sy1 + cy0 * cy1 * cx1 : null, c, k = mode === "stereographic" ? 1 / (1 + cc) : mode === "gnomonic" ? 1 / cc : mode === "equidistant" ? (c = Math.acos(cc), c ? c / Math.sin(c) : 0) : mode === "equalarea" ? Math.sqrt(2 / (1 + cc)) : 1, x = k * cy1 * sx1, y = k * (sy0 * cy1 * cx1 - cy0 * sy1); + return [ scale * x + translate[0], scale * y + translate[1] ]; + } + var mode = "orthographic", origin, scale = 200, translate = [ 480, 250 ], x0, y0, cy0, sy0; + azimuthal.invert = function(coordinates) { + var x = (coordinates[0] - translate[0]) / scale, y = (coordinates[1] - translate[1]) / scale, p = Math.sqrt(x * x + y * y), c = mode === "stereographic" ? 2 * Math.atan(p) : mode === "gnomonic" ? Math.atan(p) : mode === "equidistant" ? p : mode === "equalarea" ? 2 * Math.asin(.5 * p) : Math.asin(p), sc = Math.sin(c), cc = Math.cos(c); + return [ (x0 + Math.atan2(x * sc, p * cy0 * cc + y * sy0 * sc)) / d3_geo_radians, Math.asin(cc * sy0 - (p ? y * sc * cy0 / p : 0)) / d3_geo_radians ]; + }; + azimuthal.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return azimuthal; + }; + azimuthal.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + x0 = origin[0] * d3_geo_radians; + y0 = origin[1] * d3_geo_radians; + cy0 = Math.cos(y0); + sy0 = Math.sin(y0); + return azimuthal; + }; + azimuthal.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + return azimuthal; + }; + azimuthal.translate = function(x) { + if (!arguments.length) return translate; + translate = [ +x[0], +x[1] ]; + return azimuthal; + }; + return azimuthal.origin([ 0, 0 ]); + }; + d3.geo.albers = function() { + function albers(coordinates) { + var t = n * (d3_geo_radians * coordinates[0] - lng0), p = Math.sqrt(C - 2 * n * Math.sin(d3_geo_radians * coordinates[1])) / n; + return [ scale * p * Math.sin(t) + translate[0], scale * (p * Math.cos(t) - p0) + translate[1] ]; + } + function reload() { + var phi1 = d3_geo_radians * parallels[0], phi2 = d3_geo_radians * parallels[1], lat0 = d3_geo_radians * origin[1], s = Math.sin(phi1), c = Math.cos(phi1); + lng0 = d3_geo_radians * origin[0]; + n = .5 * (s + Math.sin(phi2)); + C = c * c + 2 * n * s; + p0 = Math.sqrt(C - 2 * n * Math.sin(lat0)) / n; + return albers; + } + var origin = [ -98, 38 ], parallels = [ 29.5, 45.5 ], scale = 1e3, translate = [ 480, 250 ], lng0, n, C, p0; + albers.invert = function(coordinates) { + var x = (coordinates[0] - translate[0]) / scale, y = (coordinates[1] - translate[1]) / scale, p0y = p0 + y, t = Math.atan2(x, p0y), p = Math.sqrt(x * x + p0y * p0y); + return [ (lng0 + t / n) / d3_geo_radians, Math.asin((C - p * p * n * n) / (2 * n)) / d3_geo_radians ]; + }; + albers.origin = function(x) { + if (!arguments.length) return origin; + origin = [ +x[0], +x[1] ]; + return reload(); + }; + albers.parallels = function(x) { + if (!arguments.length) return parallels; + parallels = [ +x[0], +x[1] ]; + return reload(); + }; + albers.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + return albers; + }; + albers.translate = function(x) { + if (!arguments.length) return translate; + translate = [ +x[0], +x[1] ]; + return albers; + }; + return reload(); + }; + d3.geo.albersUsa = function() { + function albersUsa(coordinates) { + var lon = coordinates[0], lat = coordinates[1]; + return (lat > 50 ? alaska : lon < -140 ? hawaii : lat < 21 ? puertoRico : lower48)(coordinates); + } + var lower48 = d3.geo.albers(); + var alaska = d3.geo.albers().origin([ -160, 60 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.albers().origin([ -160, 20 ]).parallels([ 8, 18 ]); + var puertoRico = d3.geo.albers().origin([ -60, 10 ]).parallels([ 8, 18 ]); + albersUsa.scale = function(x) { + if (!arguments.length) return lower48.scale(); + lower48.scale(x); + alaska.scale(x * .6); + hawaii.scale(x); + puertoRico.scale(x * 1.5); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(x) { + if (!arguments.length) return lower48.translate(); + var dz = lower48.scale() / 1e3, dx = x[0], dy = x[1]; + lower48.translate(x); + alaska.translate([ dx - 400 * dz, dy + 170 * dz ]); + hawaii.translate([ dx - 190 * dz, dy + 200 * dz ]); + puertoRico.translate([ dx + 580 * dz, dy + 430 * dz ]); + return albersUsa; + }; + return albersUsa.scale(lower48.scale()); + }; + d3.geo.bonne = function() { + function bonne(coordinates) { + var x = coordinates[0] * d3_geo_radians - x0, y = coordinates[1] * d3_geo_radians - y0; + if (y1) { + var p = c1 + y1 - y, E = x * Math.cos(y) / p; + x = p * Math.sin(E); + y = p * Math.cos(E) - c1; + } else { + x *= Math.cos(y); + y *= -1; + } + return [ scale * x + translate[0], scale * y + translate[1] ]; + } + var scale = 200, translate = [ 480, 250 ], x0, y0, y1, c1; + bonne.invert = function(coordinates) { + var x = (coordinates[0] - translate[0]) / scale, y = (coordinates[1] - translate[1]) / scale; + if (y1) { + var c = c1 + y, p = Math.sqrt(x * x + c * c); + y = c1 + y1 - p; + x = x0 + p * Math.atan2(x, c) / Math.cos(y); + } else { + y *= -1; + x /= Math.cos(y); + } + return [ x / d3_geo_radians, y / d3_geo_radians ]; + }; + bonne.parallel = function(x) { + if (!arguments.length) return y1 / d3_geo_radians; + c1 = 1 / Math.tan(y1 = x * d3_geo_radians); + return bonne; + }; + bonne.origin = function(x) { + if (!arguments.length) return [ x0 / d3_geo_radians, y0 / d3_geo_radians ]; + x0 = x[0] * d3_geo_radians; + y0 = x[1] * d3_geo_radians; + return bonne; + }; + bonne.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + return bonne; + }; + bonne.translate = function(x) { + if (!arguments.length) return translate; + translate = [ +x[0], +x[1] ]; + return bonne; + }; + return bonne.origin([ 0, 0 ]).parallel(45); + }; + d3.geo.equirectangular = function() { + function equirectangular(coordinates) { + var x = coordinates[0] / 360, y = -coordinates[1] / 360; + return [ scale * x + translate[0], scale * y + translate[1] ]; + } + var scale = 500, translate = [ 480, 250 ]; + equirectangular.invert = function(coordinates) { + var x = (coordinates[0] - translate[0]) / scale, y = (coordinates[1] - translate[1]) / scale; + return [ 360 * x, -360 * y ]; + }; + equirectangular.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + return equirectangular; + }; + equirectangular.translate = function(x) { + if (!arguments.length) return translate; + translate = [ +x[0], +x[1] ]; + return equirectangular; + }; + return equirectangular; + }; + d3.geo.mercator = function() { + function mercator(coordinates) { + var x = coordinates[0] / 360, y = -(Math.log(Math.tan(Math.PI / 4 + coordinates[1] * d3_geo_radians / 2)) / d3_geo_radians) / 360; + return [ scale * x + translate[0], scale * Math.max(-.5, Math.min(.5, y)) + translate[1] ]; + } + var scale = 500, translate = [ 480, 250 ]; + mercator.invert = function(coordinates) { + var x = (coordinates[0] - translate[0]) / scale, y = (coordinates[1] - translate[1]) / scale; + return [ 360 * x, 2 * Math.atan(Math.exp(-360 * y * d3_geo_radians)) / d3_geo_radians - 90 ]; + }; + mercator.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + return mercator; + }; + mercator.translate = function(x) { + if (!arguments.length) return translate; + translate = [ +x[0], +x[1] ]; + return mercator; + }; + return mercator; + }; + d3.geo.path = function() { + function path(d, i) { + if (typeof pointRadius === "function") pointCircle = d3_path_circle(pointRadius.apply(this, arguments)); + pathType(d); + var result = buffer.length ? buffer.join("") : null; + buffer = []; + return result; + } + function project(coordinates) { + return projection(coordinates).join(","); + } + function polygonArea(coordinates) { + var sum = area(coordinates[0]), i = 0, n = coordinates.length; + while (++i < n) sum -= area(coordinates[i]); + return sum; + } + function polygonCentroid(coordinates) { + var polygon = d3.geom.polygon(coordinates[0].map(projection)), area = polygon.area(), centroid = polygon.centroid(area < 0 ? (area *= -1, 1) : -1), x = centroid[0], y = centroid[1], z = area, i = 0, n = coordinates.length; + while (++i < n) { + polygon = d3.geom.polygon(coordinates[i].map(projection)); + area = polygon.area(); + centroid = polygon.centroid(area < 0 ? (area *= -1, 1) : -1); + x -= centroid[0]; + y -= centroid[1]; + z -= area; + } + return [ x, y, 6 * z ]; + } + function area(coordinates) { + return Math.abs(d3.geom.polygon(coordinates.map(projection)).area()); + } + var pointRadius = 4.5, pointCircle = d3_path_circle(pointRadius), projection = d3.geo.albersUsa(), buffer = []; + var pathType = d3_geo_type({ + FeatureCollection: function(o) { + var features = o.features, i = -1, n = features.length; + while (++i < n) buffer.push(pathType(features[i].geometry)); + }, + Feature: function(o) { + pathType(o.geometry); + }, + Point: function(o) { + buffer.push("M", project(o.coordinates), pointCircle); + }, + MultiPoint: function(o) { + var coordinates = o.coordinates, i = -1, n = coordinates.length; + while (++i < n) buffer.push("M", project(coordinates[i]), pointCircle); + }, + LineString: function(o) { + var coordinates = o.coordinates, i = -1, n = coordinates.length; + buffer.push("M"); + while (++i < n) buffer.push(project(coordinates[i]), "L"); + buffer.pop(); + }, + MultiLineString: function(o) { + var coordinates = o.coordinates, i = -1, n = coordinates.length, subcoordinates, j, m; + while (++i < n) { + subcoordinates = coordinates[i]; + j = -1; + m = subcoordinates.length; + buffer.push("M"); + while (++j < m) buffer.push(project(subcoordinates[j]), "L"); + buffer.pop(); + } + }, + Polygon: function(o) { + var coordinates = o.coordinates, i = -1, n = coordinates.length, subcoordinates, j, m; + while (++i < n) { + subcoordinates = coordinates[i]; + j = -1; + if ((m = subcoordinates.length - 1) > 0) { + buffer.push("M"); + while (++j < m) buffer.push(project(subcoordinates[j]), "L"); + buffer[buffer.length - 1] = "Z"; + } + } + }, + MultiPolygon: function(o) { + var coordinates = o.coordinates, i = -1, n = coordinates.length, subcoordinates, j, m, subsubcoordinates, k, p; + while (++i < n) { + subcoordinates = coordinates[i]; + j = -1; + m = subcoordinates.length; + while (++j < m) { + subsubcoordinates = subcoordinates[j]; + k = -1; + if ((p = subsubcoordinates.length - 1) > 0) { + buffer.push("M"); + while (++k < p) buffer.push(project(subsubcoordinates[k]), "L"); + buffer[buffer.length - 1] = "Z"; + } + } + } + }, + GeometryCollection: function(o) { + var geometries = o.geometries, i = -1, n = geometries.length; + while (++i < n) buffer.push(pathType(geometries[i])); + } + }); + var areaType = path.area = d3_geo_type({ + FeatureCollection: function(o) { + var area = 0, features = o.features, i = -1, n = features.length; + while (++i < n) area += areaType(features[i]); + return area; + }, + Feature: function(o) { + return areaType(o.geometry); + }, + Polygon: function(o) { + return polygonArea(o.coordinates); + }, + MultiPolygon: function(o) { + var sum = 0, coordinates = o.coordinates, i = -1, n = coordinates.length; + while (++i < n) sum += polygonArea(coordinates[i]); + return sum; + }, + GeometryCollection: function(o) { + var sum = 0, geometries = o.geometries, i = -1, n = geometries.length; + while (++i < n) sum += areaType(geometries[i]); + return sum; + } + }, 0); + var centroidType = path.centroid = d3_geo_type({ + Feature: function(o) { + return centroidType(o.geometry); + }, + Polygon: function(o) { + var centroid = polygonCentroid(o.coordinates); + return [ centroid[0] / centroid[2], centroid[1] / centroid[2] ]; + }, + MultiPolygon: function(o) { + var area = 0, coordinates = o.coordinates, centroid, x = 0, y = 0, z = 0, i = -1, n = coordinates.length; + while (++i < n) { + centroid = polygonCentroid(coordinates[i]); + x += centroid[0]; + y += centroid[1]; + z += centroid[2]; + } + return [ x / z, y / z ]; + } + }); + path.projection = function(x) { + projection = x; + return path; + }; + path.pointRadius = function(x) { + if (typeof x === "function") pointRadius = x; else { + pointRadius = +x; + pointCircle = d3_path_circle(pointRadius); + } + return path; + }; + return path; + }; + d3.geo.bounds = function(feature) { + var left = Infinity, bottom = Infinity, right = -Infinity, top = -Infinity; + d3_geo_bounds(feature, function(x, y) { + if (x < left) left = x; + if (x > right) right = x; + if (y < bottom) bottom = y; + if (y > top) top = y; + }); + return [ [ left, bottom ], [ right, top ] ]; + }; + var d3_geo_boundsTypes = { + Feature: d3_geo_boundsFeature, + FeatureCollection: d3_geo_boundsFeatureCollection, + GeometryCollection: d3_geo_boundsGeometryCollection, + LineString: d3_geo_boundsLineString, + MultiLineString: d3_geo_boundsMultiLineString, + MultiPoint: d3_geo_boundsLineString, + MultiPolygon: d3_geo_boundsMultiPolygon, + Point: d3_geo_boundsPoint, + Polygon: d3_geo_boundsPolygon + }; + d3.geo.circle = function() { + function circle() {} + function visible(point) { + return arc.distance(point) < radians; + } + function clip(coordinates) { + var i = -1, n = coordinates.length, clipped = [], p0, p1, p2, d0, d1; + while (++i < n) { + d1 = arc.distance(p2 = coordinates[i]); + if (d1 < radians) { + if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1))); + clipped.push(p2); + p0 = p1 = null; + } else { + p1 = p2; + if (!p0 && clipped.length) { + clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radians - d0) / (d1 - d0))); + p0 = p1; + } + } + d0 = d1; + } + p0 = coordinates[0]; + p1 = clipped[0]; + if (p1 && p2[0] === p0[0] && p2[1] === p0[1] && !(p2[0] === p1[0] && p2[1] === p1[1])) { + clipped.push(p1); + } + return resample(clipped); + } + function resample(coordinates) { + var i = 0, n = coordinates.length, j, m, resampled = n ? [ coordinates[0] ] : coordinates, resamples, origin = arc.source(); + while (++i < n) { + resamples = arc.source(coordinates[i - 1])(coordinates[i]).coordinates; + for (j = 0, m = resamples.length; ++j < m; ) resampled.push(resamples[j]); + } + arc.source(origin); + return resampled; + } + var origin = [ 0, 0 ], degrees = 90 - .01, radians = degrees * d3_geo_radians, arc = d3.geo.greatArc().source(origin).target(d3_identity); + circle.clip = function(d) { + if (typeof origin === "function") arc.source(origin.apply(this, arguments)); + return clipType(d) || null; + }; + var clipType = d3_geo_type({ + FeatureCollection: function(o) { + var features = o.features.map(clipType).filter(d3_identity); + return features && (o = Object.create(o), o.features = features, o); + }, + Feature: function(o) { + var geometry = clipType(o.geometry); + return geometry && (o = Object.create(o), o.geometry = geometry, o); + }, + Point: function(o) { + return visible(o.coordinates) && o; + }, + MultiPoint: function(o) { + var coordinates = o.coordinates.filter(visible); + return coordinates.length && { + type: o.type, + coordinates: coordinates + }; + }, + LineString: function(o) { + var coordinates = clip(o.coordinates); + return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o); + }, + MultiLineString: function(o) { + var coordinates = o.coordinates.map(clip).filter(function(d) { + return d.length; + }); + return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o); + }, + Polygon: function(o) { + var coordinates = o.coordinates.map(clip); + return coordinates[0].length && (o = Object.create(o), o.coordinates = coordinates, o); + }, + MultiPolygon: function(o) { + var coordinates = o.coordinates.map(function(d) { + return d.map(clip); + }).filter(function(d) { + return d[0].length; + }); + return coordinates.length && (o = Object.create(o), o.coordinates = coordinates, o); + }, + GeometryCollection: function(o) { + var geometries = o.geometries.map(clipType).filter(d3_identity); + return geometries.length && (o = Object.create(o), o.geometries = geometries, o); + } + }); + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + if (typeof origin !== "function") arc.source(origin); + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return degrees; + radians = (degrees = +x) * d3_geo_radians; + return circle; + }; + return d3.rebind(circle, arc, "precision"); + }; + d3.geo.greatArc = function() { + function greatArc() { + var d = greatArc.distance.apply(this, arguments), t = 0, dt = precision / d, coordinates = [ p0 ]; + while ((t += dt) < 1) coordinates.push(interpolate(t)); + coordinates.push(p1); + return { + type: "LineString", + coordinates: coordinates + }; + } + var source = d3_geo_greatArcSource, p0, target = d3_geo_greatArcTarget, p1, precision = 6 * d3_geo_radians, interpolate = d3_geo_greatArcInterpolator(); + greatArc.distance = function() { + if (typeof source === "function") interpolate.source(p0 = source.apply(this, arguments)); + if (typeof target === "function") interpolate.target(p1 = target.apply(this, arguments)); + return interpolate.distance(); + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _; + if (typeof source !== "function") interpolate.source(p0 = source); + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _; + if (typeof target !== "function") interpolate.target(p1 = target); + return greatArc; + }; + greatArc.precision = function(_) { + if (!arguments.length) return precision / d3_geo_radians; + precision = _ * d3_geo_radians; + return greatArc; + }; + return greatArc; + }; + d3.geo.greatCircle = d3.geo.circle; + d3.geom = {}; + d3.geom.contour = function(grid, start) { + var s = start || d3_geom_contourStart(grid), c = [], x = s[0], y = s[1], dx = 0, dy = 0, pdx = NaN, pdy = NaN, i = 0; + do { + i = 0; + if (grid(x - 1, y - 1)) i += 1; + if (grid(x, y - 1)) i += 2; + if (grid(x - 1, y)) i += 4; + if (grid(x, y)) i += 8; + if (i === 6) { + dx = pdy === -1 ? -1 : 1; + dy = 0; + } else if (i === 9) { + dx = 0; + dy = pdx === 1 ? -1 : 1; + } else { + dx = d3_geom_contourDx[i]; + dy = d3_geom_contourDy[i]; + } + if (dx != pdx && dy != pdy) { + c.push([ x, y ]); + pdx = dx; + pdy = dy; + } + x += dx; + y += dy; + } while (s[0] != x || s[1] != y); + return c; + }; + var d3_geom_contourDx = [ 1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 0, 0, -1, 0, -1, NaN ], d3_geom_contourDy = [ 0, -1, 0, 0, 0, -1, 0, 0, 1, -1, 1, 1, 0, -1, 0, NaN ]; + d3.geom.hull = function(vertices) { + if (vertices.length < 3) return []; + var len = vertices.length, plen = len - 1, points = [], stack = [], i, j, h = 0, x1, y1, x2, y2, u, v, a, sp; + for (i = 1; i < len; ++i) { + if (vertices[i][1] < vertices[h][1]) { + h = i; + } else if (vertices[i][1] == vertices[h][1]) { + h = vertices[i][0] < vertices[h][0] ? i : h; + } + } + for (i = 0; i < len; ++i) { + if (i === h) continue; + y1 = vertices[i][1] - vertices[h][1]; + x1 = vertices[i][0] - vertices[h][0]; + points.push({ + angle: Math.atan2(y1, x1), + index: i + }); + } + points.sort(function(a, b) { + return a.angle - b.angle; + }); + a = points[0].angle; + v = points[0].index; + u = 0; + for (i = 1; i < plen; ++i) { + j = points[i].index; + if (a == points[i].angle) { + x1 = vertices[v][0] - vertices[h][0]; + y1 = vertices[v][1] - vertices[h][1]; + x2 = vertices[j][0] - vertices[h][0]; + y2 = vertices[j][1] - vertices[h][1]; + if (x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2) { + points[i].index = -1; + } else { + points[u].index = -1; + a = points[i].angle; + u = i; + v = j; + } + } else { + a = points[i].angle; + u = i; + v = j; + } + } + stack.push(h); + for (i = 0, j = 0; i < 2; ++j) { + if (points[j].index !== -1) { + stack.push(points[j].index); + i++; + } + } + sp = stack.length; + for (; j < plen; ++j) { + if (points[j].index === -1) continue; + while (!d3_geom_hullCCW(stack[sp - 2], stack[sp - 1], points[j].index, vertices)) { + --sp; + } + stack[sp++] = points[j].index; + } + var poly = []; + for (i = 0; i < sp; ++i) { + poly.push(vertices[stack[i]]); + } + return poly; + }; + d3.geom.polygon = function(coordinates) { + coordinates.area = function() { + var i = 0, n = coordinates.length, a = coordinates[n - 1][0] * coordinates[0][1], b = coordinates[n - 1][1] * coordinates[0][0]; + while (++i < n) { + a += coordinates[i - 1][0] * coordinates[i][1]; + b += coordinates[i - 1][1] * coordinates[i][0]; + } + return (b - a) * .5; + }; + coordinates.centroid = function(k) { + var i = -1, n = coordinates.length, x = 0, y = 0, a, b = coordinates[n - 1], c; + if (!arguments.length) k = -1 / (6 * coordinates.area()); + while (++i < n) { + a = b; + b = coordinates[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + coordinates.clip = function(subject) { + var input, i = -1, n = coordinates.length, j, m, a = coordinates[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = coordinates[i]; + c = input[(m = input.length) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + a = b; + } + return subject; + }; + return coordinates; + }; + d3.geom.voronoi = function(vertices) { + var polygons = vertices.map(function() { + return []; + }); + d3_voronoi_tessellate(vertices, function(e) { + var s1, s2, x1, x2, y1, y2; + if (e.a === 1 && e.b >= 0) { + s1 = e.ep.r; + s2 = e.ep.l; + } else { + s1 = e.ep.l; + s2 = e.ep.r; + } + if (e.a === 1) { + y1 = s1 ? s1.y : -1e6; + x1 = e.c - e.b * y1; + y2 = s2 ? s2.y : 1e6; + x2 = e.c - e.b * y2; + } else { + x1 = s1 ? s1.x : -1e6; + y1 = e.c - e.a * x1; + x2 = s2 ? s2.x : 1e6; + y2 = e.c - e.a * x2; + } + var v1 = [ x1, y1 ], v2 = [ x2, y2 ]; + polygons[e.region.l.index].push(v1, v2); + polygons[e.region.r.index].push(v1, v2); + }); + return polygons.map(function(polygon, i) { + var cx = vertices[i][0], cy = vertices[i][1]; + polygon.forEach(function(v) { + v.angle = Math.atan2(v[0] - cx, v[1] - cy); + }); + return polygon.sort(function(a, b) { + return a.angle - b.angle; + }).filter(function(d, i) { + return !i || d.angle - polygon[i - 1].angle > 1e-10; + }); + }); + }; + var d3_voronoi_opposite = { + l: "r", + r: "l" + }; + d3.geom.delaunay = function(vertices) { + var edges = vertices.map(function() { + return []; + }), triangles = []; + d3_voronoi_tessellate(vertices, function(e) { + edges[e.region.l.index].push(vertices[e.region.r.index]); + }); + edges.forEach(function(edge, i) { + var v = vertices[i], cx = v[0], cy = v[1]; + edge.forEach(function(v) { + v.angle = Math.atan2(v[0] - cx, v[1] - cy); + }); + edge.sort(function(a, b) { + return a.angle - b.angle; + }); + for (var j = 0, m = edge.length - 1; j < m; j++) { + triangles.push([ v, edge[j], edge[j + 1] ]); + } + }); + return triangles; + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + function insert(n, p, x1, y1, x2, y2) { + if (isNaN(p.x) || isNaN(p.y)) return; + if (n.leaf) { + var v = n.point; + if (v) { + if (Math.abs(v.x - p.x) + Math.abs(v.y - p.y) < .01) { + insertChild(n, p, x1, y1, x2, y2); + } else { + n.point = null; + insertChild(n, v, x1, y1, x2, y2); + insertChild(n, p, x1, y1, x2, y2); + } + } else { + n.point = p; + } + } else { + insertChild(n, p, x1, y1, x2, y2); + } + } + function insertChild(n, p, x1, y1, x2, y2) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = p.x >= sx, bottom = p.y >= sy, i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = sx; else x2 = sx; + if (bottom) y1 = sy; else y2 = sy; + insert(n, p, x1, y1, x2, y2); + } + var p, i = -1, n = points.length; + if (n && isNaN(points[0].x)) points = points.map(d3_geom_quadtreePoint); + if (arguments.length < 5) { + if (arguments.length === 3) { + y2 = x2 = y1; + y1 = x1; + } else { + x1 = y1 = Infinity; + x2 = y2 = -Infinity; + while (++i < n) { + p = points[i]; + if (p.x < x1) x1 = p.x; + if (p.y < y1) y1 = p.y; + if (p.x > x2) x2 = p.x; + if (p.y > y2) y2 = p.y; + } + var dx = x2 - x1, dy = y2 - y1; + if (dx > dy) y2 = y1 + dx; else x2 = x1 + dy; + } + } + var root = d3_geom_quadtreeNode(); + root.add = function(p) { + insert(root, p, x1, y1, x2, y2); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2); + }; + points.forEach(root.add); + return root; + }; + d3.time = {}; + var d3_time = Date, d3_time_daySymbols = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]; + d3_time_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + var d3_time_formatDateTime = "%a %b %e %H:%M:%S %Y", d3_time_formatDate = "%m/%d/%y", d3_time_formatTime = "%H:%M:%S"; + var d3_time_days = d3_time_daySymbols, d3_time_dayAbbreviations = d3_time_days.map(d3_time_formatAbbreviate), d3_time_months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], d3_time_monthAbbreviations = d3_time_months.map(d3_time_formatAbbreviate); + d3.time.format = function(template) { + function format(date) { + var string = [], i = -1, j = 0, c, f; + while (++i < n) { + if (template.charCodeAt(i) == 37) { + string.push(template.substring(j, i), (f = d3_time_formats[c = template.charAt(++i)]) ? f(date) : c); + j = i + 1; + } + } + string.push(template.substring(j, i)); + return string.join(""); + } + var n = template.length; + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0 + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var date = new d3_time; + date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H, d.M, d.S, d.L); + return date; + }; + format.toString = function() { + return template; + }; + return format; + }; + var d3_time_zfill2 = d3.format("02d"), d3_time_zfill3 = d3.format("03d"), d3_time_zfill4 = d3.format("04d"), d3_time_sfill2 = d3.format("2d"); + var d3_time_dayRe = d3_time_formatRe(d3_time_days), d3_time_dayAbbrevRe = d3_time_formatRe(d3_time_dayAbbreviations), d3_time_monthRe = d3_time_formatRe(d3_time_months), d3_time_monthLookup = d3_time_formatLookup(d3_time_months), d3_time_monthAbbrevRe = d3_time_formatRe(d3_time_monthAbbreviations), d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations); + var d3_time_formats = { + a: function(d) { + return d3_time_dayAbbreviations[d.getDay()]; + }, + A: function(d) { + return d3_time_days[d.getDay()]; + }, + b: function(d) { + return d3_time_monthAbbreviations[d.getMonth()]; + }, + B: function(d) { + return d3_time_months[d.getMonth()]; + }, + c: d3.time.format(d3_time_formatDateTime), + d: function(d) { + return d3_time_zfill2(d.getDate()); + }, + e: function(d) { + return d3_time_sfill2(d.getDate()); + }, + H: function(d) { + return d3_time_zfill2(d.getHours()); + }, + I: function(d) { + return d3_time_zfill2(d.getHours() % 12 || 12); + }, + j: function(d) { + return d3_time_zfill3(1 + d3.time.dayOfYear(d)); + }, + L: function(d) { + return d3_time_zfill3(d.getMilliseconds()); + }, + m: function(d) { + return d3_time_zfill2(d.getMonth() + 1); + }, + M: function(d) { + return d3_time_zfill2(d.getMinutes()); + }, + p: function(d) { + return d.getHours() >= 12 ? "PM" : "AM"; + }, + S: function(d) { + return d3_time_zfill2(d.getSeconds()); + }, + U: function(d) { + return d3_time_zfill2(d3.time.sundayOfYear(d)); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d) { + return d3_time_zfill2(d3.time.mondayOfYear(d)); + }, + x: d3.time.format(d3_time_formatDate), + X: d3.time.format(d3_time_formatTime), + y: function(d) { + return d3_time_zfill2(d.getFullYear() % 100); + }, + Y: function(d) { + return d3_time_zfill4(d.getFullYear() % 1e4); + }, + Z: d3_time_zone, + "%": function(d) { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear + }; + var d3_time_numberRe = /^\s*\d+/; + var d3_time_amPmLookup = d3.map({ + am: 0, + pm: 1 + }); + d3.time.format.utc = function(template) { + function format(date) { + try { + d3_time = d3_time_utc; + var utc = new d3_time; + utc._ = date; + return local(utc); + } finally { + d3_time = Date; + } + } + var local = d3.time.format(template); + format.parse = function(string) { + try { + d3_time = d3_time_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_time = Date; + } + }; + format.toString = local.toString; + return format; + }; + var d3_time_formatIso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3.time.format.iso = Date.prototype.toISOString ? d3_time_formatIsoNative : d3_time_formatIso; + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + d3.time.second = d3_time_interval(function(date) { + return new d3_time(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3.time.seconds = d3.time.second.range; + d3.time.seconds.utc = d3.time.second.utc.range; + d3.time.minute = d3_time_interval(function(date) { + return new d3_time(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3.time.minutes = d3.time.minute.range; + d3.time.minutes.utc = d3.time.minute.utc.range; + d3.time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_time((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3.time.hours = d3.time.hour.range; + d3.time.hours.utc = d3.time.hour.utc.range; + d3.time.day = d3_time_interval(function(date) { + var day = new d3_time(1970, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3.time.days = d3.time.day.range; + d3.time.days.utc = d3.time.day.utc.range; + d3.time.dayOfYear = function(date) { + var year = d3.time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + d3_time_daySymbols.forEach(function(day, i) { + day = day.toLowerCase(); + i = 7 - i; + var interval = d3.time[day] = d3_time_interval(function(date) { + (date = d3.time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3.time.year(date).getDay(); + return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3.time[day + "s"] = interval.range; + d3.time[day + "s"].utc = interval.utc.range; + d3.time[day + "OfYear"] = function(date) { + var day = d3.time.year(date).getDay(); + return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3.time.week = d3.time.sunday; + d3.time.weeks = d3.time.sunday.range; + d3.time.weeks.utc = d3.time.sunday.utc.range; + d3.time.weekOfYear = d3.time.sundayOfYear; + d3.time.month = d3_time_interval(function(date) { + date = d3.time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3.time.months = d3.time.month.range; + d3.time.months.utc = d3.time.month.utc.range; + d3.time.year = d3_time_interval(function(date) { + date = d3.time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3.time.years = d3.time.year.range; + d3.time.years.utc = d3.time.year.utc.range; + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3.time.second, 1 ], [ d3.time.second, 5 ], [ d3.time.second, 15 ], [ d3.time.second, 30 ], [ d3.time.minute, 1 ], [ d3.time.minute, 5 ], [ d3.time.minute, 15 ], [ d3.time.minute, 30 ], [ d3.time.hour, 1 ], [ d3.time.hour, 3 ], [ d3.time.hour, 6 ], [ d3.time.hour, 12 ], [ d3.time.day, 1 ], [ d3.time.day, 2 ], [ d3.time.week, 1 ], [ d3.time.month, 1 ], [ d3.time.month, 3 ], [ d3.time.year, 1 ] ]; + var d3_time_scaleLocalFormats = [ [ d3.time.format("%Y"), function(d) { + return true; + } ], [ d3.time.format("%B"), function(d) { + return d.getMonth(); + } ], [ d3.time.format("%b %d"), function(d) { + return d.getDate() != 1; + } ], [ d3.time.format("%a %d"), function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ d3.time.format("%I %p"), function(d) { + return d.getHours(); + } ], [ d3.time.format("%I:%M"), function(d) { + return d.getMinutes(); + } ], [ d3.time.format(":%S"), function(d) { + return d.getSeconds(); + } ], [ d3.time.format(".%L"), function(d) { + return d.getMilliseconds(); + } ] ]; + var d3_time_scaleLinear = d3.scale.linear(), d3_time_scaleLocalFormat = d3_time_scaleFormat(d3_time_scaleLocalFormats); + d3_time_scaleLocalMethods.year = function(extent, m) { + return d3_time_scaleLinear.domain(extent.map(d3_time_scaleGetYear)).ticks(m).map(d3_time_scaleSetYear); + }; + d3.time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUTCMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUTCFormats = [ [ d3.time.format.utc("%Y"), function(d) { + return true; + } ], [ d3.time.format.utc("%B"), function(d) { + return d.getUTCMonth(); + } ], [ d3.time.format.utc("%b %d"), function(d) { + return d.getUTCDate() != 1; + } ], [ d3.time.format.utc("%a %d"), function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ d3.time.format.utc("%I %p"), function(d) { + return d.getUTCHours(); + } ], [ d3.time.format.utc("%I:%M"), function(d) { + return d.getUTCMinutes(); + } ], [ d3.time.format.utc(":%S"), function(d) { + return d.getUTCSeconds(); + } ], [ d3.time.format.utc(".%L"), function(d) { + return d.getUTCMilliseconds(); + } ] ]; + var d3_time_scaleUTCFormat = d3_time_scaleFormat(d3_time_scaleUTCFormats); + d3_time_scaleUTCMethods.year = function(extent, m) { + return d3_time_scaleLinear.domain(extent.map(d3_time_scaleUTCGetYear)).ticks(m).map(d3_time_scaleUTCSetYear); + }; + d3.time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUTCMethods, d3_time_scaleUTCFormat); + }; +})(); \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery-ui-timepicker-addon/jquery-ui-timepicker-addon.js b/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery-ui-timepicker-addon/jquery-ui-timepicker-addon.js new file mode 100644 index 000000000..b7e5adcf2 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery-ui-timepicker-addon/jquery-ui-timepicker-addon.js @@ -0,0 +1,2145 @@ +/*! jQuery Timepicker Addon - v1.4.3 - 2013-11-30 +* http://trentrichardson.com/examples/timepicker +* Copyright (c) 2013 Trent Richardson; Licensed MIT */ +(function ($) { + + /* + * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded" + */ + $.ui.timepicker = $.ui.timepicker || {}; + if ($.ui.timepicker.version) { + return; + } + + /* + * Extend jQueryUI, get it started with our version number + */ + $.extend($.ui, { + timepicker: { + version: "1.4.3" + } + }); + + /* + * Timepicker manager. + * Use the singleton instance of this class, $.timepicker, to interact with the time picker. + * Settings for (groups of) time pickers are maintained in an instance object, + * allowing multiple different settings on the same page. + */ + var Timepicker = function () { + this.regional = []; // Available regional settings, indexed by language code + this.regional[''] = { // Default regional settings + currentText: 'Now', + closeText: 'Done', + amNames: ['AM', 'A'], + pmNames: ['PM', 'P'], + timeFormat: 'HH:mm', + timeSuffix: '', + timeOnlyTitle: 'Choose Time', + timeText: 'Time', + hourText: 'Hour', + minuteText: 'Minute', + secondText: 'Second', + millisecText: 'Millisecond', + microsecText: 'Microsecond', + timezoneText: 'Time Zone', + isRTL: false + }; + this._defaults = { // Global defaults for all the datetime picker instances + showButtonPanel: true, + timeOnly: false, + showHour: null, + showMinute: null, + showSecond: null, + showMillisec: null, + showMicrosec: null, + showTimezone: null, + showTime: true, + stepHour: 1, + stepMinute: 1, + stepSecond: 1, + stepMillisec: 1, + stepMicrosec: 1, + hour: 0, + minute: 0, + second: 0, + millisec: 0, + microsec: 0, + timezone: null, + hourMin: 0, + minuteMin: 0, + secondMin: 0, + millisecMin: 0, + microsecMin: 0, + hourMax: 23, + minuteMax: 59, + secondMax: 59, + millisecMax: 999, + microsecMax: 999, + minDateTime: null, + maxDateTime: null, + onSelect: null, + hourGrid: 0, + minuteGrid: 0, + secondGrid: 0, + millisecGrid: 0, + microsecGrid: 0, + alwaysSetTime: true, + separator: ' ', + altFieldTimeOnly: true, + altTimeFormat: null, + altSeparator: null, + altTimeSuffix: null, + pickerTimeFormat: null, + pickerTimeSuffix: null, + showTimepicker: true, + timezoneList: null, + addSliderAccess: false, + sliderAccessArgs: null, + controlType: 'slider', + defaultValue: null, + parse: 'strict' + }; + $.extend(this._defaults, this.regional['']); + }; + + $.extend(Timepicker.prototype, { + $input: null, + $altInput: null, + $timeObj: null, + inst: null, + hour_slider: null, + minute_slider: null, + second_slider: null, + millisec_slider: null, + microsec_slider: null, + timezone_select: null, + hour: 0, + minute: 0, + second: 0, + millisec: 0, + microsec: 0, + timezone: null, + hourMinOriginal: null, + minuteMinOriginal: null, + secondMinOriginal: null, + millisecMinOriginal: null, + microsecMinOriginal: null, + hourMaxOriginal: null, + minuteMaxOriginal: null, + secondMaxOriginal: null, + millisecMaxOriginal: null, + microsecMaxOriginal: null, + ampm: '', + formattedDate: '', + formattedTime: '', + formattedDateTime: '', + timezoneList: null, + units: ['hour', 'minute', 'second', 'millisec', 'microsec'], + support: {}, + control: null, + + /* + * Override the default settings for all instances of the time picker. + * @param {Object} settings object - the new settings to use as defaults (anonymous object) + * @return {Object} the manager object + */ + setDefaults: function (settings) { + extendRemove(this._defaults, settings || {}); + return this; + }, + + /* + * Create a new Timepicker instance + */ + _newInst: function ($input, opts) { + var tp_inst = new Timepicker(), + inlineSettings = {}, + fns = {}, + overrides, i; + + for (var attrName in this._defaults) { + if (this._defaults.hasOwnProperty(attrName)) { + var attrValue = $input.attr('time:' + attrName); + if (attrValue) { + try { + inlineSettings[attrName] = eval(attrValue); + } catch (err) { + inlineSettings[attrName] = attrValue; + } + } + } + } + + overrides = { + beforeShow: function (input, dp_inst) { + if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) { + return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst); + } + }, + onChangeMonthYear: function (year, month, dp_inst) { + // Update the time as well : this prevents the time from disappearing from the $input field. + tp_inst._updateDateTime(dp_inst); + if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) { + tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst); + } + }, + onClose: function (dateText, dp_inst) { + if (tp_inst.timeDefined === true && $input.val() !== '') { + tp_inst._updateDateTime(dp_inst); + } + if ($.isFunction(tp_inst._defaults.evnts.onClose)) { + tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst); + } + } + }; + for (i in overrides) { + if (overrides.hasOwnProperty(i)) { + fns[i] = opts[i] || null; + } + } + + tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, { + evnts: fns, + timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker'); + }); + tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) { + return val.toUpperCase(); + }); + tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) { + return val.toUpperCase(); + }); + + // detect which units are supported + tp_inst.support = detectSupport( + tp_inst._defaults.timeFormat + + (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') + + (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : '')); + + // controlType is string - key to our this._controls + if (typeof(tp_inst._defaults.controlType) === 'string') { + if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') { + tp_inst._defaults.controlType = 'select'; + } + tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType]; + } + // controlType is an object and must implement create, options, value methods + else { + tp_inst.control = tp_inst._defaults.controlType; + } + + // prep the timezone options + var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60, + 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840]; + if (tp_inst._defaults.timezoneList !== null) { + timezoneList = tp_inst._defaults.timezoneList; + } + var tzl = timezoneList.length, tzi = 0, tzv = null; + if (tzl > 0 && typeof timezoneList[0] !== 'object') { + for (; tzi < tzl; tzi++) { + tzv = timezoneList[tzi]; + timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) }; + } + } + tp_inst._defaults.timezoneList = timezoneList; + + // set the default units + tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) : + ((new Date()).getTimezoneOffset() * -1); + tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin : + tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour; + tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin : + tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute; + tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin : + tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second; + tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin : + tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec; + tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin : + tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec; + tp_inst.ampm = ''; + tp_inst.$input = $input; + + if (tp_inst._defaults.altField) { + tp_inst.$altInput = $(tp_inst._defaults.altField).css({ + cursor: 'pointer' + }).focus(function () { + $input.trigger("focus"); + }); + } + + if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) { + tp_inst._defaults.minDate = new Date(); + } + if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) { + tp_inst._defaults.maxDate = new Date(); + } + + // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime.. + if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) { + tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime()); + } + if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) { + tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime()); + } + if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) { + tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime()); + } + if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) { + tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime()); + } + tp_inst.$input.bind('focus', function () { + tp_inst._onFocus(); + }); + + return tp_inst; + }, + + /* + * add our sliders to the calendar + */ + _addTimePicker: function (dp_inst) { + var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val(); + + this.timeDefined = this._parseTime(currDT); + this._limitMinMaxDateTime(dp_inst, false); + this._injectTimePicker(); + }, + + /* + * parse the time string from input value or _setTime + */ + _parseTime: function (timeString, withDate) { + if (!this.inst) { + this.inst = $.datepicker._getInst(this.$input[0]); + } + + if (withDate || !this._defaults.timeOnly) { + var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat'); + try { + var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults); + if (!parseRes.timeObj) { + return false; + } + $.extend(this, parseRes.timeObj); + } catch (err) { + $.timepicker.log("Error parsing the date/time string: " + err + + "\ndate/time string = " + timeString + + "\ntimeFormat = " + this._defaults.timeFormat + + "\ndateFormat = " + dp_dateFormat); + return false; + } + return true; + } else { + var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults); + if (!timeObj) { + return false; + } + $.extend(this, timeObj); + return true; + } + }, + + /* + * generate and inject html for timepicker into ui datepicker + */ + _injectTimePicker: function () { + var $dp = this.inst.dpDiv, + o = this.inst.settings, + tp_inst = this, + litem = '', + uitem = '', + show = null, + max = {}, + gridSize = {}, + size = null, + i = 0, + l = 0; + + // Prevent displaying twice + if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) { + var noDisplay = ' style="display:none;"', + html = '
    ' + '
    ' + o.timeText + '
    ' + + '
    '; + + // Create the markup + for (i = 0, l = this.units.length; i < l; i++) { + litem = this.units[i]; + uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1); + show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem]; + + // Added by Peter Medeiros: + // - Figure out what the hour/minute/second max should be based on the step values. + // - Example: if stepMinute is 15, then minMax is 45. + max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10); + gridSize[litem] = 0; + + html += '
    ' + o[litem + 'Text'] + '
    ' + + '
    '; + + if (show && o[litem + 'Grid'] > 0) { + html += '
    '; + + if (litem === 'hour') { + for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) { + gridSize[litem]++; + var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o); + html += ''; + } + } + else { + for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) { + gridSize[litem]++; + html += ''; + } + } + + html += '
    ' + tmph + '' + ((m < 10) ? '0' : '') + m + '
    '; + } + html += '
    '; + } + + // Timezone + var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone; + html += '
    ' + o.timezoneText + '
    '; + html += '
    '; + + // Create the elements from string + html += '
    '; + var $tp = $(html); + + // if we only want time picker... + if (o.timeOnly === true) { + $tp.prepend('
    ' + '
    ' + o.timeOnlyTitle + '
    ' + '
    '); + $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide(); + } + + // add sliders, adjust grids, add events + for (i = 0, l = tp_inst.units.length; i < l; i++) { + litem = tp_inst.units[i]; + uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1); + show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem]; + + // add the slider + tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]); + + // adjust the grid and add click event + if (show && o[litem + 'Grid'] > 0) { + size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']); + $tp.find('.ui_tpicker_' + litem + ' table').css({ + width: size + "%", + marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"), + marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0', + borderCollapse: 'collapse' + }).find("td").click(function (e) { + var $t = $(this), + h = $t.html(), + n = parseInt(h.replace(/[^0-9]/g), 10), + ap = h.replace(/[^apm]/ig), + f = $t.data('for'); // loses scope, so we use data-for + + if (f === 'hour') { + if (ap.indexOf('p') !== -1 && n < 12) { + n += 12; + } + else { + if (ap.indexOf('a') !== -1 && n === 12) { + n = 0; + } + } + } + + tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n); + + tp_inst._onTimeChange(); + tp_inst._onSelectHandler(); + }).css({ + cursor: 'pointer', + width: (100 / gridSize[litem]) + '%', + textAlign: 'center', + overflow: 'hidden' + }); + } // end if grid > 0 + } // end for loop + + // Add timezone options + this.timezone_select = $tp.find('.ui_tpicker_timezone').append('').find("select"); + $.fn.append.apply(this.timezone_select, + $.map(o.timezoneList, function (val, idx) { + return $("
    ',image:'',iframe:'",error:'

    The requested content cannot be loaded.
    Please try again later.

    ',closeBtn:'',next:'',prev:''},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0, +openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1, +isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=u(a)?f(a).get():[a]),f.each(a,function(e,c){var l={},g,h,k,n,m;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),u(c)?(l={href:c.data("fancybox-href")||c.attr("href"),title:f("
    ").text(c.data("fancybox-title")||c.attr("title")).html(),isDom:!0,element:c}, +f.metadata&&f.extend(!0,l,c.metadata())):l=c);g=d.href||l.href||(r(c)?c:null);h=d.title!==w?d.title:l.title||"";n=(k=d.content||l.content)?"html":d.type||l.type;!n&&l.isDom&&(n=c.data("fancybox-type"),n||(n=(n=c.prop("class").match(/fancybox\.(\w+)/))?n[1]:null));r(g)&&(n||(b.isImage(g)?n="image":b.isSWF(g)?n="swf":"#"===g.charAt(0)?n="inline":r(c)&&(n="html",k=c)),"ajax"===n&&(m=g.split(/\s+/,2),g=m.shift(),m=m.shift()));k||("inline"===n?g?k=f(r(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):l.isDom&&(k=c): +"html"===n?k=g:n||g||!l.isDom||(n="inline",k=c));f.extend(l,{href:g,type:n,content:k,title:h,selector:m});a[e]=l}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==w&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1===b.trigger("onCancel")||(b.hideLoading(),a&&(b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(), +b.coming=null,b.current||b._afterZoomOut(a)))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(b.isOpen&&!0!==a?(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]()):(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&& +(b.player.timer=setTimeout(b.next,b.current.playSpeed))},c=function(){d();p.unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};!0===a||!b.player.isActive&&!1!==a?b.current&&(b.current.loop||b.current.index=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==w&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,l;c&&(l=b._getPosition(d),a&&"scroll"===a.type?(delete l.position,c.stop(!0,!0).animate(l,200)):(c.css(l),e.pos=f.extend({},e.dim,l)))}, +update:function(a){var d=a&&a.originalEvent&&a.originalEvent.type,e=!d||"orientationchange"===d;e&&(clearTimeout(C),C=null);b.isOpen&&!C&&(C=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),C=null)},e&&!t?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,t&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"), +b.trigger("onUpdate")),b.update())},hideLoading:function(){p.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('
    ').click(b.cancel).appendTo("body");p.bind("keydown.loading",function(a){27===(a.which||a.keyCode)&&(a.preventDefault(),b.cancel())});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}));b.trigger("onLoading")},getViewport:function(){var a=b.current&& +b.current.locked||!1,d={x:q.scrollLeft(),y:q.scrollTop()};a&&a.length?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=t&&s.innerWidth?s.innerWidth:q.width(),d.h=t&&s.innerHeight?s.innerHeight:q.height());return d},unbindEvents:function(){b.wrap&&u(b.wrap)&&b.wrap.unbind(".fb");p.unbind(".fb");q.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(q.bind("orientationchange.fb"+(t?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&p.bind("keydown.fb",function(e){var c= +e.which||e.keyCode,l=e.target||e.srcElement;if(27===c&&b.coming)return!1;e.ctrlKey||e.altKey||e.shiftKey||e.metaKey||l&&(l.type||f(l).is("[contenteditable]"))||f.each(d,function(d,l){if(1h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();0!==c&&!k&&1g||0>l)&&b.next(0>g?"up":"right"),d.preventDefault())}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d,e){if(e&& +b.helpers[d]&&f.isFunction(b.helpers[d][a]))b.helpers[d][a](f.extend(!0,{},b.helpers[d].defaults,e),c)})}p.trigger(a)},isImage:function(a){return r(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)},isSWF:function(a){return r(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=m(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&&(d.padding=[c,c, +c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive=!0;if("image"=== +c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&t&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(t?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,x(d.padding[a]))});b.trigger("onReady"); +if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width= +this.width/b.opts.pixelRatio;b.coming.height=this.height/b.opts.pixelRatio;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming, +d=f(a.tpl.iframe.replace(/\{rnd\}/g,(new Date).getTime())).attr("scrolling",t?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);t||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload|| +b._afterLoad()},_preloadImages:function(){var a=b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,l,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove()); +b.unbindEvents();e=a.content;c=a.type;l=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("
    ").html(e).find(a.selector):u(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('
    ').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder", +!1)}));break;case "image":e=a.tpl.image.replace(/\{href\}/g,g);break;case "swf":e='',h="",f.each(a.swf,function(a,b){e+='';h+=" "+a+'="'+b+'"'}),e+='"}u(e)&&e.parent().is(a.inner)||a.inner.append(e);b.trigger("beforeShow"); +a.inner.css("overflow","yes"===l?"scroll":"no"===l?"hidden":l);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(!b.isOpened)f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();else if(d.prevMethod)b.transitions[d.prevMethod]();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,l=b.skin,g=b.inner,h=b.current,c=h.width,k=h.height,n=h.minWidth,v=h.minHeight,p=h.maxWidth, +q=h.maxHeight,t=h.scrolling,r=h.scrollOutside?h.scrollbarWidth:0,y=h.margin,z=m(y[1]+y[3]),s=m(y[0]+y[2]),w,A,u,D,B,G,C,E,I;e.add(l).add(g).width("auto").height("auto").removeClass("fancybox-tmp");y=m(l.outerWidth(!0)-l.width());w=m(l.outerHeight(!0)-l.height());A=z+y;u=s+w;D=F(c)?(a.w-A)*m(c)/100:c;B=F(k)?(a.h-u)*m(k)/100:k;if("iframe"===h.type){if(I=h.content,h.autoHeight&&1===I.data("ready"))try{I[0].contentWindow.document.location&&(g.width(D).height(9999),G=I.contents().find("body"),r&&G.css("overflow-x", +"hidden"),B=G.outerHeight(!0))}catch(H){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(D),h.autoHeight||g.height(B),h.autoWidth&&(D=g.width()),h.autoHeight&&(B=g.height()),g.removeClass("fancybox-tmp");c=m(D);k=m(B);E=D/B;n=m(F(n)?m(n,"w")-A:n);p=m(F(p)?m(p,"w")-A:p);v=m(F(v)?m(v,"h")-u:v);q=m(F(q)?m(q,"h")-u:q);G=p;C=q;h.fitToView&&(p=Math.min(a.w-A,p),q=Math.min(a.h-u,q));A=a.w-z;s=a.h-s;h.aspectRatio?(c>p&&(c=p,k=m(c/E)),k>q&&(k=q,c=m(k*E)),cA||z>s)&&c>n&&k>v&&!(19p&&(c=p,k=m(c/E)),g.width(c).height(k),e.width(c+y),a=e.width(),z=e.height();else c=Math.max(n,Math.min(c,c-(a-A))),k=Math.max(v,Math.min(k,k-(z-s)));r&&"auto"===t&&kA||z>s)&&c>n&&k>v;c=h.aspectRatio?cv&&k
    ').appendTo(d&&d.lenth?d:"body");this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(q.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay", +function(a){if(f(a.target).hasClass("fancybox-overlay"))return b.isActive?b.close():d.close(),!1});this.overlay.css(a.css).show()},close:function(){q.unbind("resize.overlay");this.el.hasClass("fancybox-lock")&&(f(".fancybox-margin").removeClass("fancybox-margin"),this.el.removeClass("fancybox-lock"),q.scrollTop(this.scrollV).scrollLeft(this.scrollH));f(".fancybox-overlay").remove().hide();f.extend(this,{overlay:null,fixed:!1})},update:function(){var a="100%",b;this.overlay.width(a).height("100%"); +J?(b=Math.max(H.documentElement.offsetWidth,H.body.offsetWidth),p.width()>b&&(a=p.width())):p.width()>q.width()&&(a=p.width());this.overlay.width(a).height(p.height())},onReady:function(a,b){var e=this.overlay;f(".fancybox-overlay").stop(!0,!0);e||this.create(a);a.locked&&this.fixed&&b.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&!this.el.hasClass("fancybox-lock")&&(!1!==this.fixPosition&&f("*").filter(function(){return"fixed"=== +f(this).css("position")&&!f(this).hasClass("fancybox-overlay")&&!f(this).hasClass("fancybox-wrap")}).addClass("fancybox-margin"),this.el.addClass("fancybox-margin"),this.scrollV=q.scrollTop(),this.scrollH=q.scrollLeft(),this.el.addClass("fancybox-lock"),q.scrollTop(this.scrollV).scrollLeft(this.scrollH));this.open(a)},onUpdate:function(){this.fixed||this.update()},afterClose:function(a){this.overlay&&!b.coming&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float", +position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(r(e)&&""!==f.trim(e)){d=f('
    '+e+"
    ");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"),J&&d.width(d.width()),d.wrapInner(''),b.current.margin[2]+=Math.abs(m(d.css("margin-bottom")))}d["top"===a.position?"prependTo": +"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",l=function(g){var h=f(this).blur(),k=d,l,m;g.ctrlKey||g.altKey||g.shiftKey||g.metaKey||h.is(".fancybox-wrap")||(l=a.groupAttr||"data-fancybox-group",m=h.attr(l),m||(l="rel",m=h.get(0)[l]),m&&""!==m&&"nofollow"!==m&&(h=c.length?f(c):e,h=h.filter("["+l+'="'+m+'"]'),k=h.index(this)),a.index=k,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;c&&!1!==a.live?p.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')", +"click.fb-start",l):e.unbind("click.fb-start").bind("click.fb-start",l);this.filter("[data-fancybox-start=1]").trigger("click");return this};p.ready(function(){var a,d;f.scrollbarWidth===w&&(f.scrollbarWidth=function(){var a=f('
    ').appendTo("body"),b=a.children(),b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});f.support.fixedPosition===w&&(f.support.fixedPosition=function(){var a=f('
    ').appendTo("body"), +b=20===a[0].offsetTop||15===a[0].offsetTop;a.remove();return b}());f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")});a=f(s).width();K.addClass("fancybox-lock-test");d=f(s).width();K.removeClass("fancybox-lock-test");f("").appendTo("head")})})(window,document,jQuery); \ No newline at end of file diff --git a/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_myddleware/function.js b/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_myddleware/function.js new file mode 100644 index 000000000..9b8e2d68e --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_myddleware/function.js @@ -0,0 +1,6 @@ + $( document ).ready(function() { + + $('.switch').onoff(); + $(".fancybox").fancybox(); + +}); // jquery diff --git a/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_onoff/jquery.onoff.min.js b/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_onoff/jquery.onoff.min.js new file mode 100644 index 000000000..b4aaf6323 --- /dev/null +++ b/src/Myddleware/RegleBundle/Resources/public/js/lib/jquery_onoff/jquery.onoff.min.js @@ -0,0 +1,4 @@ +/** jquery.onoff - v0.3.6 - 2014-06-23 +* https://github.com/timmywil/jquery.onoff +* Copyright (c) 2014 Timmy Willison; Licensed MIT */ +!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):"object"==typeof exports?b(require("jquery")):b(a.jQuery)}(this,function(a){"use strict";function b(c,d){if(!(this instanceof b))return new b(c,d);if("input"!==c.nodeName.toLowerCase()||"checkbox"!==c.type)return a.error("OnOff should be called on checkboxes");var e=a.data(c,b.datakey);return e?e:(this.options=d=a.extend({},b.defaults,d),this.elem=c,this.$elem=a(c).addClass(d.className),this.$doc=a(c.ownerDocument||document),d.namespace+=a.guid++,c.id||(c.id="onoffswitch"+g++),this.enable(),a.data(c,b.datakey,this),void 0)}var c="over out down up move enter leave cancel".split(" "),d=a.extend({},a.event.mouseHooks),e={};if(window.PointerEvent)a.each(c,function(b,c){a.event.fixHooks[e[c]="pointer"+c]=d});else{var f=d.props;d.props=f.concat(["touches","changedTouches","targetTouches","altKey","ctrlKey","metaKey","shiftKey"]),d.filter=function(a,b){var c,d=f.length;if(!b.pageX&&b.touches&&(c=b.touches[0]))for(;d--;)a[f[d]]=c[f[d]];return a},a.each(c,function(b,c){if(2>b)e[c]="mouse"+c;else{var f="touch"+("down"===c?"start":"up"===c?"end":c);a.event.fixHooks[f]=d,e[c]=f+" mouse"+c}})}a.pointertouch=e;var g=1,h=Array.prototype.slice;return b.datakey="_onoff",b.defaults={namespace:".onoff",className:"onoffswitch-checkbox"},b.prototype={constructor:b,instance:function(){return this},wrap:function(){var b=this.elem,c=this.$elem,d=this.options,e=c.parent(".onoffswitch");e.length||(c.wrap('
    '),e=c.parent().addClass(b.className.replace(d.className,""))),this.$con=e;var f=c.next('label[for="'+b.id+'"]');f.length||(f=a("