commit eb914b54f97921c5008547f53afd87c63e082558 Author: inghamn Date: Fri Dec 18 12:56:12 2009 +0000 Created the new PHP branch git-svn-id: https://rosehill.googlecode.com/svn/branches/php@2 100bd78a-fc82-11de-b5bc-ffd2847a4b57 diff --git a/GPL.txt b/GPL.txt new file mode 100644 index 0000000..dba13ed --- /dev/null +++ b/GPL.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3da701d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,67 @@ +Copyright (C) 2006-2008 City of Bloomington, Indiana. All rights reserved. + +This Scaffolding is free software; you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + + +This Scaffolding 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 scaffolding; if not, you can get a copy at: +http://www.gnu.org/copyleft/gpl.html + +Or, write to +Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor +Boston, MA 02110-1301, USA + + +--------------------------------------------------------------------- + Yahoo's YUI Javascript Toolkit +--------------------------------------------------------------------- +This scaffolding includes an installation of the YUI Javascript Toolkit +The YUI Toolkit is released under the BSD license + +Software License Agreement (BSD License) +Copyright (c) 2008, Yahoo! Inc. +All rights reserved. + +Redistribution and use of this software in source and binary forms, with or +without modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of Yahoo! Inc. nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission of Yahoo! Inc. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/access_control.inc b/access_control.inc new file mode 100644 index 0000000..272b986 --- /dev/null +++ b/access_control.inc @@ -0,0 +1,33 @@ + + */ +$ZEND_ACL = new Zend_Acl(); +/** + * Load the roles from the database + */ +$roles = new RoleList(); +$roles->find(); +foreach ($roles as $role) { + $ZEND_ACL = $ZEND_ACL->addRole(new Zend_Acl_Role($role->getName())); +} + +/** + * Declare all the resources + */ +$ZEND_ACL->add(new Zend_Acl_Resource('Users')); +$ZEND_ACL->add(new Zend_Acl_Resource('Deeds')); +$ZEND_ACL->add(new Zend_Acl_Resource('Internments')); +$ZEND_ACL->add(new Zend_Acl_Resource('Cemeteries')); + + +/** + * Assign permissions to the resources + */ +// Administrator is allowed access to everything +$ZEND_ACL->allow('Administrator'); + +$ZEND_ACL->allow('Editor','Deeds'); +$ZEND_ACL->allow('Editor','Internments'); \ No newline at end of file diff --git a/blocks/html/deeds/addDeedForm.inc b/blocks/html/deeds/addDeedForm.inc new file mode 100644 index 0000000..3aafcb9 --- /dev/null +++ b/blocks/html/deeds/addDeedForm.inc @@ -0,0 +1,98 @@ + + */ +?> +

Add Deed

+
+
Deed Info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ + + +
+
\ No newline at end of file diff --git a/blocks/html/deeds/deedList.inc b/blocks/html/deeds/deedList.inc new file mode 100644 index 0000000..9076c14 --- /dev/null +++ b/blocks/html/deeds/deedList.inc @@ -0,0 +1,58 @@ + + */ +?> +
+

+ + Add + + "; + } + ?> + Deeds +

+ + + + + + + + + + + + deedList as $deed) { + $editButton = ''; + if (userIsAllowed('Deeds')) { + $url = new URL(BASE_URL.'/deeds/updateDeed.php'); + $url->id = $deed->getId(); + $editButton = " + + "; + } + $section = View::escape($deed->getSection()); + $lot = View::escape($deed->getLot()); + $owner1 = View::escape($deed->getOwner(1)); + $owner2 = View::escape($deed->getOwner(2)); + $date = $deed->getIssueDate('n/j/Y'); + echo " + + + +
Section, LotOwner 1Owner 2Issue DateCemetery
$editButton{$deed->getSection()}, {$deed->getLot()} + "; + } + ?> +
+
\ No newline at end of file diff --git a/blocks/html/deeds/updateDeedForm.inc b/blocks/html/deeds/updateDeedForm.inc new file mode 100644 index 0000000..3470484 --- /dev/null +++ b/blocks/html/deeds/updateDeedForm.inc @@ -0,0 +1,99 @@ + + */ +?> +

Update Deed

+
+
Deed Info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ + + +
+
\ No newline at end of file diff --git a/blocks/html/errorMessages.inc b/blocks/html/errorMessages.inc new file mode 100644 index 0000000..bdfce52 --- /dev/null +++ b/blocks/html/errorMessages.inc @@ -0,0 +1,25 @@ + + * @param array $this->errorMessages + */ +?> +
+

No That's wrong

+ errorMessages as $e) + { + $error = $e->getMessage(); + if (file_exists(APPLICATION_HOME."/blocks/html/errorMessages/$error.inc")) + { + include APPLICATION_HOME."/blocks/html/errorMessages/$error.inc"; + } + else + { + echo "

$error

"; + } + } + ?> +
\ No newline at end of file diff --git a/blocks/html/errorMessages/invalidDate.inc b/blocks/html/errorMessages/invalidDate.inc new file mode 100644 index 0000000..b959ba4 --- /dev/null +++ b/blocks/html/errorMessages/invalidDate.inc @@ -0,0 +1 @@ +

You provided an invalid date.

diff --git a/blocks/html/errorMessages/invalidLogin.inc b/blocks/html/errorMessages/invalidLogin.inc new file mode 100644 index 0000000..dd769d3 --- /dev/null +++ b/blocks/html/errorMessages/invalidLogin.inc @@ -0,0 +1 @@ +

The username and password combination you entered is not correct.

diff --git a/blocks/html/errorMessages/ldap/unknownUser.inc b/blocks/html/errorMessages/ldap/unknownUser.inc new file mode 100644 index 0000000..ff8ae69 --- /dev/null +++ b/blocks/html/errorMessages/ldap/unknownUser.inc @@ -0,0 +1 @@ +

That username is not in LDAP. You cannot set LDAP authentication if the user is not in LDAP.

diff --git a/blocks/html/errorMessages/missingRequiredFields.inc b/blocks/html/errorMessages/missingRequiredFields.inc new file mode 100644 index 0000000..31f6f43 --- /dev/null +++ b/blocks/html/errorMessages/missingRequiredFields.inc @@ -0,0 +1 @@ +

You did not include all the required fields.

diff --git a/blocks/html/errorMessages/noAccessAllowed.inc b/blocks/html/errorMessages/noAccessAllowed.inc new file mode 100644 index 0000000..286d7bc --- /dev/null +++ b/blocks/html/errorMessages/noAccessAllowed.inc @@ -0,0 +1 @@ +

Sorry, you're not allowed to go there.

diff --git a/blocks/html/errorMessages/notLoggedIn.inc b/blocks/html/errorMessages/notLoggedIn.inc new file mode 100644 index 0000000..06d4ae7 --- /dev/null +++ b/blocks/html/errorMessages/notLoggedIn.inc @@ -0,0 +1 @@ +

You are not logged into this site anymore.

diff --git a/blocks/html/errorMessages/passwordIsCorrupted.inc b/blocks/html/errorMessages/passwordIsCorrupted.inc new file mode 100644 index 0000000..dd27de4 --- /dev/null +++ b/blocks/html/errorMessages/passwordIsCorrupted.inc @@ -0,0 +1 @@ +

Your password is corrupted. Please contact a system administrator for help.

diff --git a/blocks/html/errorMessages/unknownUser.inc b/blocks/html/errorMessages/unknownUser.inc new file mode 100644 index 0000000..41a9c7e --- /dev/null +++ b/blocks/html/errorMessages/unknownUser.inc @@ -0,0 +1 @@ +

That username is not in our system

diff --git a/blocks/html/errorMessages/users/missingPerson_id.inc b/blocks/html/errorMessages/users/missingPerson_id.inc new file mode 100644 index 0000000..b9abca8 --- /dev/null +++ b/blocks/html/errorMessages/users/missingPerson_id.inc @@ -0,0 +1 @@ +

You can only create user accounts for people who are already in the system, or are in LDAP.

diff --git a/blocks/html/errorMessages/wrongPassword.inc b/blocks/html/errorMessages/wrongPassword.inc new file mode 100644 index 0000000..25b0063 --- /dev/null +++ b/blocks/html/errorMessages/wrongPassword.inc @@ -0,0 +1 @@ +

The password you entered is not correct.

diff --git a/blocks/html/loginForm.inc b/blocks/html/loginForm.inc new file mode 100644 index 0000000..2600378 --- /dev/null +++ b/blocks/html/loginForm.inc @@ -0,0 +1,15 @@ +
+
Login + + + + + + + + + +
+ +
+
diff --git a/blocks/html/people/addPersonForm.inc b/blocks/html/people/addPersonForm.inc new file mode 100644 index 0000000..5cec339 --- /dev/null +++ b/blocks/html/people/addPersonForm.inc @@ -0,0 +1,35 @@ + + */ +?> +

Add Person

+
+
Personal Info + + + + + + + + + + + + + + +
+
+
+
+ + + +
+
diff --git a/blocks/html/people/personInfo.inc b/blocks/html/people/personInfo.inc new file mode 100644 index 0000000..58a390e --- /dev/null +++ b/blocks/html/people/personInfo.inc @@ -0,0 +1,29 @@ + + * @param Person $this->person + */ +$username = $this->person->getUsername(); +if (!$username && userIsAllowed('Users')) { + $url = BASE_URL.'/users/addUser.php?person_id='.$this->person->getId(); + $username = " + "; +} + + +$name = View::escape($this->person->getFullname()); +echo " +

$name

+ + + + + + + +
Username$username
Email{$this->person->getEmail()}
+"; diff --git a/blocks/html/people/personList.inc b/blocks/html/people/personList.inc new file mode 100644 index 0000000..7d81224 --- /dev/null +++ b/blocks/html/people/personList.inc @@ -0,0 +1,46 @@ + + * @param PersonList $this->personList + */ +?> +
+

+ Add A Person + + "; + } + ?> + People +

+ + personList as $person) { + $editButton = ''; + + if (userIsAllowed('Users')) { + $editButton = " + + "; + + } + + $name = View::escape($person->getFullname()); + echo " + + + + + + "; + } + ?> +
$editButton{$person->getUsername()}getURL()}\">$name{$person->getEmail()}
+
diff --git a/blocks/html/people/updatePersonForm.inc b/blocks/html/people/updatePersonForm.inc new file mode 100644 index 0000000..d91b4c1 --- /dev/null +++ b/blocks/html/people/updatePersonForm.inc @@ -0,0 +1,37 @@ + + * @param Person $this->person + */ +?> +

Update Person

+
+
Person Info + + + + + + + + + + + + + + + +
+
+
+
+ + + +
+
diff --git a/blocks/html/users/addUserForm.inc b/blocks/html/users/addUserForm.inc new file mode 100644 index 0000000..300e890 --- /dev/null +++ b/blocks/html/users/addUserForm.inc @@ -0,0 +1,52 @@ + + */ +?> +

New User Account

+
+
Login Info + + + + + + + + + + + + + +
+
+
+
+ + + +
+
diff --git a/blocks/html/users/updateUserForm.inc b/blocks/html/users/updateUserForm.inc new file mode 100644 index 0000000..b1d1fba --- /dev/null +++ b/blocks/html/users/updateUserForm.inc @@ -0,0 +1,53 @@ + + * @param User $this->user + */ +?> +

Edit user->getUsername(); ?>

+
+
Login Info + + + + + + + + + + + + + + +
+
+
+
+ + +
+
diff --git a/blocks/html/users/userList.inc b/blocks/html/users/userList.inc new file mode 100644 index 0000000..e612676 --- /dev/null +++ b/blocks/html/users/userList.inc @@ -0,0 +1,57 @@ + + * @param UserList $this->userList + */ +?> +
+

+ Add User Account + + "; + } + ?> + User Accounts +

+ + userList as $user) { + $editButton = ''; + $deleteButton = ''; + if (userIsAllowed('Users')) { + $editButton = " + + "; + + $deleteButton = " + + "; + } + + echo " + + + + + + + "; + } + ?> +
$editButton{$user->getUsername()}{$user->getFirstname()} {$user->getLastname()}{$user->getAuthenticationMethod()} + "; + foreach ($user->getRoles() as $role) { + echo "$role "; + } + echo " +
+
diff --git a/classes/Deed.php b/classes/Deed.php new file mode 100644 index 0000000..6462b4b --- /dev/null +++ b/classes/Deed.php @@ -0,0 +1,380 @@ + + */ +class Deed +{ + private $id; + private $section; + private $lot; + private $lastname1; + private $firstname1; + private $middleInitial1; + private $lastname2; + private $firstname2; + private $middleInitial2; + private $issueDate; + private $notes; + private $lot2; + private $whiteoak; + + /** + * Populates the object with data + * + * Passing in an associative array of data will populate this object without + * hitting the database. + * + * Passing in a scalar will load the data from the database. + * This will load all fields in the table as properties of this class. + * You may want to replace this with, or add your own extra, custom loading + * + * @param int|array $id + */ + public function __construct($id=null) + { + if ($id) { + if (is_array($id)) { + $result = $id; + } + else { + $zend_db = Database::getConnection(); + $sql = 'select * from deeds where id=?'; + $result = $zend_db->fetchRow($sql,array($id)); + } + + if ($result) { + foreach ($result as $field=>$value) { + if ($value) { + if ($field == 'issueDate') { + $value = (false === strpos($value,'0000')) ? new Date($value) : null; + } + $this->$field = $value; + } + } + } + else { + throw new Exception('deeds/unknownDeed'); + } + } + else { + // This is where the code goes to generate a new, empty instance. + // Set any default values for properties that need it here + } + } + + /** + * Throws an exception if anything's wrong + * @throws Exception $e + */ + public function validate() + { + // Check for required fields here. Throw an exception if anything is missing. + + } + + /** + * Saves this record back to the database + */ + public function save() + { + $this->validate(); + + $data = array(); + $data['section'] = $this->section ? $this->section : null; + $data['lot'] = $this->lot ? $this->lot : null; + $data['lastname1'] = $this->lastname1 ? $this->lastname1 : null; + $data['firstname1'] = $this->firstname1 ? $this->firstname1 : null; + $data['middleInitial1'] = $this->middleInitial1 ? $this->middleInitial1 : null; + $data['lastname2'] = $this->lastname2 ? $this->lastname2 : null; + $data['firstname2'] = $this->firstname2 ? $this->firstname2 : null; + $data['middleInitial2'] = $this->middleInitial2 ? $this->middleInitial2 : null; + $data['issueDate'] = $this->issueDate ? $this->issueDate->format('Y-m-d') : null; + $data['notes'] = $this->notes ? $this->notes : null; + $data['lot2'] = $this->lot2 ? $this->lot2 : null; + $data['whiteoak'] = $this->whiteoak ? $this->whiteoak : null; + + if ($this->id) { + $this->update($data); + } + else { + $this->insert($data); + } + } + + private function update($data) + { + $zend_db = Database::getConnection(); + $zend_db->update('deeds',$data,"id='{$this->id}'"); + } + + private function insert($data) + { + $zend_db = Database::getConnection(); + $zend_db->insert('deeds',$data); + $this->id = $zend_db->lastInsertId('deeds','id'); + } + + //---------------------------------------------------------------- + // Generic Getters + //---------------------------------------------------------------- + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getSection() + { + return $this->section; + } + + /** + * @return string + */ + public function getLot() + { + return $this->lot; + } + + /** + * @return string + */ + public function getLastname1() + { + return $this->lastname1; + } + + /** + * @return string + */ + public function getFirstname1() + { + return $this->firstname1; + } + + /** + * @return char + */ + public function getMiddleInitial1() + { + return $this->middleInitial1; + } + + /** + * @return string + */ + public function getLastname2() + { + return $this->lastname2; + } + + /** + * @return string + */ + public function getFirstname2() + { + return $this->firstname2; + } + + /** + * @return char + */ + public function getMiddleInitial2() + { + return $this->middleInitial2; + } + + /** + * Returns the date/time in the desired format + * + * Format is specified using PHP's date() syntax + * http://www.php.net/manual/en/function.date.php + * If no format is given, the Date object is returned + * + * @param string $format + * @return string|DateTime + */ + public function getIssueDate($format=null) + { + if ($format && $this->issueDate) { + return $this->issueDate->format($format); + } + else { + return $this->issueDate; + } + } + + /** + * @return text + */ + public function getNotes() + { + return $this->notes; + } + + /** + * @return char + */ + public function getLot2() + { + return $this->lot2; + } + + /** + * @return char + */ + public function getWhiteoak() + { + return $this->whiteoak; + } + + //---------------------------------------------------------------- + // Generic Setters + //---------------------------------------------------------------- + + /** + * @param string $string + */ + public function setSection($string) + { + $this->section = trim($string); + } + + /** + * @param string $string + */ + public function setLot($string) + { + $this->lot = trim($string); + } + + /** + * @param string $string + */ + public function setLastname1($string) + { + $this->lastname1 = trim($string); + } + + /** + * @param string $string + */ + public function setFirstname1($string) + { + $this->firstname1 = trim($string); + } + + /** + * @param char $char + */ + public function setMiddleInitial1($char) + { + $this->middleInitial1 = $char; + } + + /** + * @param string $string + */ + public function setLastname2($string) + { + $this->lastname2 = trim($string); + } + + /** + * @param string $string + */ + public function setFirstname2($string) + { + $this->firstname2 = trim($string); + } + + /** + * @param char $char + */ + public function setMiddleInitial2($char) + { + $this->middleInitial2 = $char; + } + + /** + * Sets the date + * + * Date arrays should match arrays produced by getdate() + * + * Date string formats should be in something strtotime() understands + * http://www.php.net/manual/en/function.strtotime.php + * + * @param int|string|array $date + */ + public function setIssueDate($date) + { + if ($date) { + $this->issueDate = new Date($date); + } + else { + $this->issueDate = null; + } + } + + /** + * @param text $text + */ + public function setNotes($text) + { + $this->notes = $text; + } + + /** + * @param char $char + */ + public function setLot2($char) + { + $this->lot2 = $char; + } + + /** + * @param char $char + */ + public function setWhiteoak($char) + { + $this->whiteoak = $char; + } + + + //---------------------------------------------------------------- + // Custom Functions + // We recommend adding all your custom code down here at the bottom + //---------------------------------------------------------------- + /** + * Returns the full name of the owner + * + * @return string + */ + public function getOwner($number=1) + { + $first = "firstname$number"; + $middle = "middleInitial$number"; + $last = "lastname$number"; + + $name = array(); + if ($this->$first) { + $name[] = $this->$first; + } + if ($this->$middle) { + $name[] = $this->$middle; + } + if ($this->$last) { + $name[] = $this->$last; + } + + return implode(' ',$name); + } +} diff --git a/classes/DeedList.php b/classes/DeedList.php new file mode 100644 index 0000000..984d2a3 --- /dev/null +++ b/classes/DeedList.php @@ -0,0 +1,87 @@ + + */ +class DeedList extends ZendDbResultIterator +{ + /** + * Creates a basic select statement for the collection. + * + * Populates the collection if you pass in $fields + * Setting itemsPerPage turns on pagination mode + * In pagination mode, this will only load the results for one page + * + * @param array $fields + * @param int $itemsPerPage Turns on Pagination + * @param int $currentPage + */ + public function __construct($fields=null,$itemsPerPage=null,$currentPage=null) + { + parent::__construct($itemsPerPage,$currentPage); + if (is_array($fields)) { + $this->find($fields); + } + } + + /** + * Populates the collection + * + * @param array $fields + * @param string|array $order Multi-column sort should be given as an array + * @param int $limit + * @param string|array $groupBy Multi-column group by should be given as an array + */ + public function find($fields=null,$order='id',$limit=null,$groupBy=null) + { + $this->select->from('deeds'); + + // Finding on fields from the deed table is handled here + if (count($fields)) { + foreach ($fields as $key=>$value) { + $this->select->where("$key=?",$value); + } + } + + // Finding on fields from other tables requires joining those tables. + // You can handle fields from other tables by adding the joins here + // If you add more joins you probably want to make sure that the + // above foreach only handles fields from the deed table. + + $this->select->order($order); + if ($limit) { + $this->select->limit($limit); + } + if ($groupBy) { + $this->select->group($groupBy); + } + $this->populateList(); + } + + /** + * Hydrates all the Deed objects from a database result set + * + * This is a callback function, called from ZendDbResultIterator. It is + * called once per row of the result. + * + * @param int $key The index of the result row to load + * @return Deed + */ + protected function loadResult($key) + { + return new Deed($this->result[$key]); + } +} diff --git a/classes/Internment.php b/classes/Internment.php new file mode 100644 index 0000000..0ea53bd --- /dev/null +++ b/classes/Internment.php @@ -0,0 +1,410 @@ + + */ +class Internment +{ + private $id; + private $section; + private $lot; + private $book; + private $pageNumber; + private $deceasedDate; + private $lastname; + private $firstname; + private $middleInitial; + private $birthPlace; + private $lastResidence; + private $age; + private $sex; + private $whiteoak; + private $notes; + private $lot2; + + /** + * Populates the object with data + * + * Passing in an associative array of data will populate this object without + * hitting the database. + * + * Passing in a scalar will load the data from the database. + * This will load all fields in the table as properties of this class. + * You may want to replace this with, or add your own extra, custom loading + * + * @param int|array $id + */ + public function __construct($id=null) + { + if ($id) { + if (is_array($id)) { + $result = $id; + } + else { + $zend_db = Database::getConnection(); + $sql = 'select * from internments where id=?'; + $result = $zend_db->fetchRow($sql,array($id)); + } + + if ($result) { + foreach ($result as $field=>$value) { + if ($value) { + if ($field == 'deceasedDate') { + $value = (false === strpos($value,'0000')) ? new Date($value) : null; + } + $this->$field = $value; + } + } + } + else { + throw new Exception('internments/unknownInternment'); + } + } + else { + // This is where the code goes to generate a new, empty instance. + // Set any default values for properties that need it here + } + } + + /** + * Throws an exception if anything's wrong + * @throws Exception $e + */ + public function validate() + { + // Check for required fields here. Throw an exception if anything is missing. + + } + + /** + * Saves this record back to the database + */ + public function save() + { + $this->validate(); + + $data = array(); + $data['section'] = $this->section ? $this->section : null; + $data['lot'] = $this->lot ? $this->lot : null; + $data['book'] = $this->book ? $this->book : null; + $data['pageNumber'] = $this->pageNumber ? $this->pageNumber : null; + $data['deceasedDate'] = $this->deceasedDate ? $this->deceasedDate->format('Y-m-d') : null; + $data['lastname'] = $this->lastname ? $this->lastname : null; + $data['firstname'] = $this->firstname ? $this->firstname : null; + $data['middleInitial'] = $this->middleInitial ? $this->middleInitial : null; + $data['birthPlace'] = $this->birthPlace ? $this->birthPlace : null; + $data['lastResidence'] = $this->lastResidence ? $this->lastResidence : null; + $data['age'] = $this->age ? $this->age : null; + $data['sex'] = $this->sex ? $this->sex : null; + $data['whiteoak'] = $this->whiteoak ? $this->whiteoak : null; + $data['notes'] = $this->notes ? $this->notes : null; + $data['lot2'] = $this->lot2 ? $this->lot2 : null; + + if ($this->id) { + $this->update($data); + } + else { + $this->insert($data); + } + } + + private function update($data) + { + $zend_db = Database::getConnection(); + $zend_db->update('internments',$data,"id='{$this->id}'"); + } + + private function insert($data) + { + $zend_db = Database::getConnection(); + $zend_db->insert('internments',$data); + $this->id = $zend_db->lastInsertId('internments','id'); + } + + //---------------------------------------------------------------- + // Generic Getters + //---------------------------------------------------------------- + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getSection() + { + return $this->section; + } + + /** + * @return string + */ + public function getLot() + { + return $this->lot; + } + + /** + * @return string + */ + public function getBook() + { + return $this->book; + } + + /** + * @return string + */ + public function getPageNumber() + { + return $this->pageNumber; + } + + /** + * Returns the date/time in the desired format + * + * Format is specified using PHP's date() syntax + * http://www.php.net/manual/en/function.date.php + * If no format is given, the Date object is returned + * + * @param string $format + * @return string|DateTime + */ + public function getDeceasedDate($format=null) + { + if ($format && $this->deceasedDate) { + return $this->deceasedDate->format($format); + } + else { + return $this->deceasedDate; + } + } + + /** + * @return string + */ + public function getLastname() + { + return $this->lastname; + } + + /** + * @return string + */ + public function getFirstname() + { + return $this->firstname; + } + + /** + * @return char + */ + public function getMiddleInitial() + { + return $this->middleInitial; + } + + /** + * @return string + */ + public function getBirthPlace() + { + return $this->birthPlace; + } + + /** + * @return string + */ + public function getLastResidence() + { + return $this->lastResidence; + } + + /** + * @return int + */ + public function getAge() + { + return $this->age; + } + + /** + * @return string + */ + public function getSex() + { + return $this->sex; + } + + /** + * @return char + */ + public function getWhiteoak() + { + return $this->whiteoak; + } + + /** + * @return text + */ + public function getNotes() + { + return $this->notes; + } + + /** + * @return string + */ + public function getLot2() + { + return $this->lot2; + } + + //---------------------------------------------------------------- + // Generic Setters + //---------------------------------------------------------------- + + /** + * @param string $string + */ + public function setSection($string) + { + $this->section = trim($string); + } + + /** + * @param string $string + */ + public function setLot($string) + { + $this->lot = trim($string); + } + + /** + * @param string $string + */ + public function setBook($string) + { + $this->book = trim($string); + } + + /** + * @param string $string + */ + public function setPageNumber($string) + { + $this->pageNumber = trim($string); + } + + /** + * Sets the date + * + * Date arrays should match arrays produced by getdate() + * + * Date string formats should be in something strtotime() understands + * http://www.php.net/manual/en/function.strtotime.php + * + * @param int|string|array $date + */ + public function setDeceasedDate($date) + { + if ($date) { + $this->deceasedDate = new Date($date); + } + else { + $this->deceasedDate = null; + } + } + + /** + * @param string $string + */ + public function setLastname($string) + { + $this->lastname = trim($string); + } + + /** + * @param string $string + */ + public function setFirstname($string) + { + $this->firstname = trim($string); + } + + /** + * @param char $char + */ + public function setMiddleInitial($char) + { + $this->middleInitial = $char; + } + + /** + * @param string $string + */ + public function setBirthPlace($string) + { + $this->birthPlace = trim($string); + } + + /** + * @param string $string + */ + public function setLastResidence($string) + { + $this->lastResidence = trim($string); + } + + /** + * @param int $int + */ + public function setAge($int) + { + $this->age = preg_replace("/[^0-9]/","",$int); + } + + /** + * @param string $string + */ + public function setSex($string) + { + $this->sex = trim($string); + } + + /** + * @param char $char + */ + public function setWhiteoak($char) + { + $this->whiteoak = $char; + } + + /** + * @param text $text + */ + public function setNotes($text) + { + $this->notes = $text; + } + + /** + * @param string $string + */ + public function setLot2($string) + { + $this->lot2 = trim($string); + } + + + //---------------------------------------------------------------- + // Custom Functions + // We recommend adding all your custom code down here at the bottom + //---------------------------------------------------------------- +} diff --git a/classes/InternmentList.php b/classes/InternmentList.php new file mode 100644 index 0000000..7a3d880 --- /dev/null +++ b/classes/InternmentList.php @@ -0,0 +1,87 @@ + + */ +class InternmentList extends ZendDbResultIterator +{ + /** + * Creates a basic select statement for the collection. + * + * Populates the collection if you pass in $fields + * Setting itemsPerPage turns on pagination mode + * In pagination mode, this will only load the results for one page + * + * @param array $fields + * @param int $itemsPerPage Turns on Pagination + * @param int $currentPage + */ + public function __construct($fields=null,$itemsPerPage=null,$currentPage=null) + { + parent::__construct($itemsPerPage,$currentPage); + if (is_array($fields)) { + $this->find($fields); + } + } + + /** + * Populates the collection + * + * @param array $fields + * @param string|array $order Multi-column sort should be given as an array + * @param int $limit + * @param string|array $groupBy Multi-column group by should be given as an array + */ + public function find($fields=null,$order='id',$limit=null,$groupBy=null) + { + $this->select->from('internments'); + + // Finding on fields from the internment table is handled here + if (count($fields)) { + foreach ($fields as $key=>$value) { + $this->select->where("$key=?",$value); + } + } + + // Finding on fields from other tables requires joining those tables. + // You can handle fields from other tables by adding the joins here + // If you add more joins you probably want to make sure that the + // above foreach only handles fields from the internment table. + + $this->select->order($order); + if ($limit) { + $this->select->limit($limit); + } + if ($groupBy) { + $this->select->group($groupBy); + } + $this->populateList(); + } + + /** + * Hydrates all the Internment objects from a database result set + * + * This is a callback function, called from ZendDbResultIterator. It is + * called once per row of the result. + * + * @param int $key The index of the result row to load + * @return Internment + */ + protected function loadResult($key) + { + return new Internment($this->result[$key]); + } +} diff --git a/classes/Person.php b/classes/Person.php new file mode 100644 index 0000000..21252d4 --- /dev/null +++ b/classes/Person.php @@ -0,0 +1,226 @@ + + */ +class Person +{ + private $id; + private $firstname; + private $lastname; + private $email; + + private $user_id; + private $user; + + /** + * Populates the object with data + * + * Passing in an associative array of data will populate this object without + * hitting the database. + * + * Passing in a scalar will load the data from the database. + * This will load all fields in the table as properties of this class. + * You may want to replace this with, or add your own extra, custom loading + * + * @param int|string|array $id (ID, email, username) + */ + public function __construct($id=null) + { + if ($id) { + if (is_array($id)) { + $result = $id; + } + else { + $zend_db = Database::getConnection(); + if (ctype_digit($id)) { + $sql = 'select * from people where id=?'; + } + elseif (false !== strpos($id,'@')) { + $sql = 'select * from people where email=?'; + } + else { + $sql = 'select p.* from people p left join users on p.id=person_id where username=?'; + } + $result = $zend_db->fetchRow($sql,array($id)); + } + + if ($result) { + foreach ($result as $field=>$value) { + if ($value) { + $this->$field = $value; + } + } + } + else { + throw new Exception('people/unknownPerson'); + } + } + else { + // This is where the code goes to generate a new, empty instance. + // Set any default values for properties that need it here + } + } + + /** + * Throws an exception if anything's wrong + * @throws Exception $e + */ + public function validate() + { + // Check for required fields here. Throw an exception if anything is missing. + if (!$this->firstname || !$this->lastname) { + throw new Exception('missingRequiredFields'); + } + } + + /** + * Saves this record back to the database + */ + public function save() + { + $this->validate(); + + $data = array(); + $data['firstname'] = $this->firstname; + $data['lastname'] = $this->lastname; + $data['email'] = $this->email ? $this->email : null; + + if ($this->id) { + $this->update($data); + } + else { + $this->insert($data); + } + } + + private function update($data) + { + $zend_db = Database::getConnection(); + $zend_db->update('people',$data,"id={$this->id}"); + } + + private function insert($data) + { + $zend_db = Database::getConnection(); + $zend_db->insert('people',$data); + $this->id = $zend_db->lastInsertId('people','id'); + } + + //---------------------------------------------------------------- + // Generic Getters + //---------------------------------------------------------------- + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getFirstname() + { + return $this->firstname; + } + + /** + * @return string + */ + public function getLastname() + { + return $this->lastname; + } + + /** + * @return string + */ + public function getEmail() + { + return $this->email; + } + + //---------------------------------------------------------------- + // Generic Setters + //---------------------------------------------------------------- + /** + * @param string $string + */ + public function setFirstname($string) + { + $this->firstname = trim($string); + } + + /** + * @param string $string + */ + public function setLastname($string) + { + $this->lastname = trim($string); + } + + /** + * @param string $string + */ + public function setEmail($string) + { + $this->email = trim($string); + } + + + //---------------------------------------------------------------- + // Custom Functions + // We recommend adding all your custom code down here at the bottom + //---------------------------------------------------------------- + /** + * @return string + */ + public function getFullname() + { + return "{$this->firstname} {$this->lastname}"; + } + + /** + * @return string + */ + public function getURL() + { + return BASE_URL.'/people/viewPerson.php?person_id='.$this->id; + } + + /** + * @return int + */ + public function getUser_id() + { + if (!$this->user_id) { + $zend_db = Database::getConnection(); + $this->user_id = $zend_db->fetchOne('select id from users where person_id=?',$this->id); + } + return $this->user_id; + } + + /** + * @return User + */ + public function getUser() { + if (!$this->user) { + if ($this->getUser_id()) { + $this->user = new User($this->getUser_id()); + } + } + return $this->user; + } + + /** + * @return string + */ + public function getUsername() { + if ($this->getUser()) { + return $this->getUser()->getUsername(); + } + } +} diff --git a/classes/PersonList.php b/classes/PersonList.php new file mode 100644 index 0000000..9a1f093 --- /dev/null +++ b/classes/PersonList.php @@ -0,0 +1,59 @@ + + */ +class PersonList extends ZendDbResultIterator +{ + /** + * @param array $fields + */ + public function __construct($fields=null) + { + parent::__construct(); + if (is_array($fields)) { + $this->find($fields); + } + } + + /** + * Populates the collection + * + * @param array $fields + * @param string|array $order Multi-column sort should be given as an array + * @param int $limit + * @param string|array $groupBy Multi-column group by should be given as an array + */ + public function find($fields=null,$order='lastname',$limit=null,$groupBy=null) + { + $this->select->from('people'); + + if (count($fields)) { + foreach ($fields as $key=>$value) { + $this->select->where("$key=?",$value); + } + } + + $this->select->order($order); + if ($limit) { + $this->select->limit($limit); + } + if ($groupBy) { + $this->select->group($groupBy); + } + $this->populateList(); + } + + /** + * Loads a single Person object for the row returned from ZendDbResultIterator + * + * @param array $key + */ + protected function loadResult($key) + { + return new Person($this->result[$key]); + } +} diff --git a/classes/Role.php b/classes/Role.php new file mode 100644 index 0000000..a320664 --- /dev/null +++ b/classes/Role.php @@ -0,0 +1,142 @@ + + */ +class Role +{ + private $id; + private $name; + + /** + * Passing in an associative array of data will populate this object without + * hitting the database. + * + * Passing in an int will load the data from the database for the given ID. + * + * This will load all fields in the table as properties of this class. + * You may want to replace this with, or add your own extra, custom loading + * + * @param int|string|array $id + */ + public function __construct($id=null) + { + if ($id) { + if (is_array($id)) { + $result = $id; + } + else { + $zend_db = Database::getConnection(); + + if (is_int($id) || ctype_digit($id)) { + $sql = 'select * from roles where id=?'; + } + else { + $sql = 'select * from roles where name=?'; + } + $result = $zend_db->fetchRow($sql,array($id)); + } + if ($result) { + foreach ($result as $field=>$value) { + if ($value) { + $this->$field = $value; + } + } + } + else { + throw new Exception('roles/unknownRole'); + } + } + else { + // This is where the code goes to generate a new, empty instance. + // Set any default values for properties that need it here + } + } + + /** + * Throws an exception if theres anything wrong + * @throws Exception + */ + public function validate() + { + if (!$this->name) { + throw new Exception('missingName'); + } + } + + /** + * This generates generic SQL that should work right away. + * You can replace this $fields code with your own custom SQL + * for each property of this class, + */ + public function save() + { + $this->validate(); + + $data = array(); + $data['name'] = $this->name; + + + if ($this->id) { + $this->update($data); + } + else { + $this->insert($data); + } + } + + private function update($data) + { + $zend_db = Database::getConnection(); + $zend_db->update('roles',$data,"id={$this->id}"); + } + + private function insert($data) + { + $zend_db = Database::getConnection(); + $zend_db->insert('roles',$data); + $this->id = $zend_db->lastInsertId('roles','id'); + } + + //---------------------------------------------------------------- + // Generic Getters + //---------------------------------------------------------------- + /** + * @return int + */ + public function getId() + { + return $this->id; + } + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + //---------------------------------------------------------------- + // Generic Setters + //---------------------------------------------------------------- + /** + * @param string $string + */ + public function setName($string) + { + $this->name = trim($string); + } + + //---------------------------------------------------------------- + // Custom Functions + // We recommend adding all your custom code down here at the bottom + //---------------------------------------------------------------- + /** + * @return string + */ + public function __toString() + { + return $this->name; + } +} diff --git a/classes/RoleList.php b/classes/RoleList.php new file mode 100644 index 0000000..52af461 --- /dev/null +++ b/classes/RoleList.php @@ -0,0 +1,58 @@ + +*/ +class RoleList extends ZendDbResultIterator +{ + /** + * @param array $fields + */ + public function __construct($fields=null) + { + parent::__construct(); + + if (is_array($fields)) { + $this->find($fields); + } + } + + /** + * Populates the collection + * + * @param array $fields + * @param string|array $order Multi-column sort should be given as an array + * @param int $limit + * @param string|array $groupBy Multi-column group by should be given as an array + */ + public function find($fields=null,$order='name',$limit=null,$groupBy=null) + { + $this->select->from('roles'); + + if (count($fields)) { + foreach ($fields as $key=>$value) { + $this->select->where("$key=?",$value); + } + } + + $this->select->order($order); + if ($limit) { + $this->select->limit($limit); + } + if ($groupBy) { + $this->select->group($groupBy); + } + $this->populateList(); + } + + /** + * Load each Role object as we iterate through the results + * + * @return array An array of Role objects + */ + protected function loadResult($key) + { + return new Role($this->result[$key]); + } +} diff --git a/classes/User.php b/classes/User.php new file mode 100644 index 0000000..dcb4cef --- /dev/null +++ b/classes/User.php @@ -0,0 +1,373 @@ + + */ +class User extends SystemUser +{ + private $id; + private $person_id; + private $username; + private $password; + private $authenticationMethod; + + private $person; + private $roles = array(); + private $newPassword; // the User's new password, unencrypted + + /** + * @param int|string $id + */ + public function __construct($id = null) + { + if ($id) { + if (is_array($id)) { + $result = $id; + } + else { + if (ctype_digit($id)) { + $sql = 'select * from users where id=?'; + } + else { + $sql = 'select * from users where username=?'; + } + $zend_db = Database::getConnection(); + $result = $zend_db->fetchRow($sql,array($id)); + } + if ($result) { + foreach ($result as $field=>$value) { + if ($value) { + $this->$field = $value; + } + } + } + else { + throw new Exception('users/unknownUser'); + } + } + else { + // This is where the code goes to generate a new, empty instance. + // Set any default values for properties that need it here + } + } + + /** + * Throws an exception if anything's wrong + * @throws Exception $e + */ + public function validate() + { + if (!$this->person_id) { + throw new Exception('users/missingPerson_id'); + } + if (!$this->username) { + throw new Exception('users/missingUsername'); + } + + } + + /** + * Saves this record back to the database + * + * This generates generic SQL that should work right away. + * You can replace this $fields code with your own custom SQL + * for each property of this class, + */ + public function save() + { + $this->validate(); + + $data = array(); + $data['person_id'] = $this->person_id; + $data['username'] = $this->username; + // Passwords should not be updated by default. Use the savePassword() function + $data['authenticationMethod'] = $this->authenticationMethod + ? $this->authenticationMethod + : null; + + // Do the database calls + if ($this->id) { + $this->update($data); + } + else { + $this->insert($data); + } + + // Save the password only if it's changed + if ($this->passwordHasChanged()) { + $this->savePassword(); + } + + $this->updateRoles(); + } + + private function update($data) + { + $zend_db = Database::getConnection(); + $zend_db->update('users',$data,"id={$this->id}"); + } + + private function insert($data) + { + $zend_db = Database::getConnection(); + $zend_db->insert('users',$data); + $this->id = $zend_db->lastInsertId('users','id'); + } + + /** + * Removes this object from the database + */ + public function delete() + { + $zend_db = Database::getConnection(); + $zend_db->delete('user_roles',"user_id={$this->id}"); + $zend_db->delete('users',"id={$this->id}"); + } + + //---------------------------------------------------------------- + // Generic Getters + //---------------------------------------------------------------- + /** + * @return int + */ + public function getId() + { + return $this->id; + } + /** + * @return int + */ + public function getPerson_id() + { + return $this->person_id; + } + /** + * @return string + */ + public function getUsername() + { + return $this->username; + } + /** + * @return string + */ + public function getAuthenticationMethod() + { + return $this->authenticationMethod; + } + /** + * @return Person + */ + public function getPerson() + { + if ($this->person_id) { + if (!$this->person) { + $this->person = new Person($this->person_id); + } + return $this->person; + } + return null; + } + + //---------------------------------------------------------------- + // Generic Setters + //---------------------------------------------------------------- + /** + * @param int $int + */ + public function setPerson_id($int) + { + $this->person = new Person($int); + $this->person_id = $int; + } + /** + * @param string $string + */ + public function setUsername($string) + { + $this->username = trim($string); + } + /** + * Takes a user-given password and converts it to an MD5 Hash + * @param String $string + */ + public function setPassword($string) + { + // Save the user given password, so we can update it externally, if needed + $this->newPassword = trim($string); + $this->password = md5(trim($string)); + } + /** + * Takes a pre-existing MD5 hash + * @param MD5 $hash + */ + public function setPasswordHash($hash) + { + $this->password = trim($hash); + } + /** + * @param string $authenticationMethod + */ + public function setAuthenticationMethod($string) + { + $this->authenticationMethod = $string; + if ($this->authenticationMethod != 'local') { + $this->password = null; + $this->saveLocalPassword(); + } + } + /** + * @param Person $person + */ + public function setPerson($person) + { + $this->person_id = $person->getId(); + $this->person = $person; + } + + //---------------------------------------------------------------- + // Custom Functions + // We recommend adding all your custom code down here at the bottom + //---------------------------------------------------------------- + /** + * @return string + */ + public function getFirstname() + { + return $this->getPerson()->getFirstname(); + } + /** + * @return string + */ + public function getLastname() + { + return $this->getPerson()->getLastname(); + } + /** + * @return string + */ + public function getEmail() + { + return $this->getPerson()->getEmail(); + } + /** + * Returns an array of Role names with the role id as the array index + * + * @return array + */ + public function getRoles() + { + if (!count($this->roles)) { + if ($this->id) { + $zend_db = Database::getConnection(); + $select = new Zend_Db_Select($zend_db); + $select->from('user_roles','role_id') + ->joinLeft('roles','role_id=id','name') + ->where('user_id=?'); + $result = $zend_db->fetchAll($select,$this->id); + + foreach ($result as $row) { + $this->roles[$row['role_id']] = $row['name']; + } + } + } + return $this->roles; + } + /** + * Takes an array of role names. Loads the Roles from the database + * + * @param array $roleNames An array of names + */ + public function setRoles($roleNames) + { + $this->roles = array(); + foreach ($roleNames as $name) { + $role = new Role($name); + $this->roles[$role->getId()] = $role->getName(); + } + } + /** + * Takes a string or an array of strings and checks if the user has that role + * + * @param Array|String $roles + * @return boolean + */ + public function hasRole($roles) + { + if (is_array($roles)) { + foreach ($roles as $roleName) { + if (in_array($roleName,$this->getRoles())) { + return true; + } + } + return false; + } + else { + return in_array($roles,$this->getRoles()); + } + } + + /** + * Saves the current roles back to the database + */ + private function updateRoles() + { + $zend_db = Database::getConnection(); + + $roles = $this->getRoles(); + + $zend_db->delete('user_roles',"user_id={$this->id}"); + + foreach ($roles as $id=>$name) { + $data = array('user_id'=>$this->id,'role_id'=>$id); + $zend_db->insert('user_roles',$data); + } + } + + /** + * Since passwords can be stored externally, we only want to bother trying + * to save them when they've actually changed + * @return boolean + */ + public function passwordHasChanged() + { + return $this->newPassword ? true : false; + } + + /** + * Callback function from the SystemUser class + * The SystemUser will determine where the password should be stored. + * If the password is stored locally, it will call this function + */ + protected function saveLocalPassword() + { + if ($this->id) { + $zend_db = Database::getConnection(); + + // Passwords in the class should already be MD5 hashed + $zend_db->update('users',array('password'=>$this->password),"id={$this->id}"); + } + } + + /** + * Callback function from the SystemUser class + * + * The SystemUser class will determine where the authentication + * should occur. If the user should be authenticated locally, + * this function will be called. + * + * @param string $password + * @return boolean + */ + protected function authenticateDatabase($password) + { + $zend_db = Database::getConnection(); + + $md5 = md5($password); + + $id = $zend_db->fetchOne('select id from users where username=? and password=?', + array($this->username,$md5)); + return $id ? true : false; + } +} diff --git a/classes/UserList.php b/classes/UserList.php new file mode 100644 index 0000000..acc9f31 --- /dev/null +++ b/classes/UserList.php @@ -0,0 +1,112 @@ + + */ +class UserList extends ZendDbResultIterator +{ + private $columns = array('id','person_id','username','password','authenticationMethod'); + + /** + * @param array $fields + */ + public function __construct($fields=null) + { + parent::__construct(); + + if (is_array($fields)) { + $this->find($fields); + } + } + + /** + * Populates the collection + * + * @param array $fields + * @param string|array $order Multi-column sort should be given as an array + * @param int $limit + * @param string|array $groupBy Multi-column group by should be given as an array + */ + public function find($fields=null,$order='username',$limit=null,$groupBy=null) + { + $this->select->from(array('u'=>'users')); + + // Finding on fields from the Users table is handled here + if (count($fields)) { + foreach ($fields as $key=>$value) { + if (array_key_exists($key,$this->columns)) { + $this->select->where("u.$key=?",$value); + } + } + } + + // Finding on fields from other tables requires joining those tables. + // You can handle fields from other tables by adding the joins here + // If you add more joins you probably want to make sure that the + // above foreach only handles fields from the users table. + $joins = array(); + + // Firstname, lastname, and email come from the People table + if (isset($fields['firstname'])) { + $joins['p'] = array('table'=>'people','condition'=>'u.id=p.user_id'); + $this->select->where('p.firstname=?',$fields['firstname']); + } + if (isset($fields['lastname'])) { + $joins['p'] = array('table'=>'people','condition'=>'u.id=p.user_id'); + $this->select->where('p.lastname=?',$fields['lastname']); + } + if (isset($fields['email'])) { + $joins['p'] = array('table'=>'people','condition'=>'u.id=p.user_id'); + $this->select->where('p.email=?',$fields['email']); + } + + // To get the Role, we have to join the user_roles and roles tables + if (isset($fields['role'])) { + $joins['ur'] = array('table'=>'user_roles','condition'=>'u.id=ur.user_id'); + $joins['r'] = array('table'=>'roles','condition'=>'ur.role_id=r.id'); + $this->select->where('r.name=?',$fields['role']); + } + + // Add all the joins we've created to the select + foreach ($joins as $key=>$join) { + $this->select->joinLeft(array($key=>$join['table']),$join['condition']); + } + + + + $this->select->order($order); + if ($limit) { + $this->select->limit($limit); + } + if ($groupBy) { + $this->select->group($groupBy); + } + $this->populateList(); + } + + /** + * Hydrates all the objects from a database result set + * + * This is a callback function, called from ZendDbResultIterator. It is + * called once per row of the result. + * + * @param int $key The index of the result row to load + * @return User + */ + protected function loadResult($key) + { + return new User($this->result[$key]); + } +} diff --git a/configuration.inc.default b/configuration.inc.default new file mode 100644 index 0000000..1d5762b --- /dev/null +++ b/configuration.inc.default @@ -0,0 +1,124 @@ + + */"); +/** + * Used to keep sessions on the same webserver seperate; + */ +define('APPLICATION_NAME','application_name'); + +/** + * Where on the filesystem this application is installed + */ +define('APPLICATION_HOME','/var/www/sites/application_name'); + +/** + * Where on the filesystem the framework is installed. + */ +define('FRAMEWORK',APPLICATION_HOME.'/libraries/framework'); + +/** + * This needs to point to the library directory inside your + * installation of the ZendFramework + * http://framework.zend.com + */ +define('ZEND',APPLICATION_HOME.'/libraries/ZendFramework/library'); +ini_set('include_path','.'.PATH_SEPARATOR.ZEND); +require_once 'Zend/Loader/Autoloader.php'; +Zend_Loader_Autoloader::getInstance(); + +/** + * The URL to get to this site + * Do NOT use a trailing slash + */ +define('BASE_URL','http://localhost'); + +/** + * Used when there's an error on the site. The Framework will + * print out a nice error message, encouraging users to report any problems + * See: FRAMEWORK/ITSFunctions.inc + * + * This is also the default Admin user information that gets added to the database + */ +define('ADMINISTRATOR_NAME','Site Admin'); +define('ADMINISTRATOR_EMAIL','admin@servername.com'); + +/** + * Set how we want to handle errors + * PHP_DEFAULT - do whatever's configured in php.ini + * + * If you do not define error handling to PHP_DEFAULT + * the custom error handlers kick in. All of the custom error display + * frunctions are in FRAMEWORK/globalFunctions.inc. The custom error + * function decide what to do based on $ERROR_REPORING array values + * + * PRETTY_PRINT - Display a message in the browser + * EMAIL_ADMIN - email the Administrator + * EMAIL_USER - email the logged in user + * SKIDDER - post errors to a Skidder server (see config below) +*/ +define('ERROR_REPORTING','PHP_DEFAULT'); +//define('ERROR_REPORTING','CUSTOM'); +//$ERROR_REPORTING = array('PRETTY_PRINT','SKIDDER'); +/** + * Skidder is a web service for error notifications. Error reporting supports + * posting errors to a Skidder server. You must register for an application_id + * on the skidder server you want to post errors to. + */ +//define('SKIDDER_URL','http://localhost/skidder/home.php'); +//define('SKIDDER_APPLICATION_ID',); + +/** + * Database Setup + * Refer to the PDO documentation for DSN sytnax for your database type + * http://www.php.net/manual/en/pdo.drivers.php + */ +define('DB_ADAPTER','Pdo_Mysql'); +define('DB_HOST','localhost'); +define('DB_NAME',APPLICATION_NAME); +define('DB_USER',APPLICATION_NAME); +define('DB_PASS','password'); + +/** + * LDAP Configuration + * This is required in order to use the LDAP authentication + * If you do not want to use LDAP authentication, you can comment this out + */ +define('LDAP_DOMAIN','ldap.domain.somewhere'); +define('LDAP_DN','ou=people,o='.LDAP_DOMAIN); +define('LDAP_USERNAME_ATTRIBUTE','uid'); +define('LDAP_ADMIN_USER','username'); +define('LDAP_ADMIN_PASS','password'); +define('LDAP_SERVER','ldap.somewhere.com'); +define('LDAP_PASSWORD_ATTRIBUTE','userpassword'); + +/** + * Import global functions that we use for many applications we write + */ +include FRAMEWORK.'/globalFunctions.php'; +spl_autoload_register('autoload'); + +/** + * Session Startup + * Don't start a session for CLI usage. + * We only want sessions when PHP code is executed from the webserver + */ +if (!defined('STDIN')) { + ini_set('session.save_path',APPLICATION_HOME.'/data/sessions'); + session_start(); +} + +/** + * Load the Zend_Acl + * Access control is going to handled using the Zend_Acl + * We only need to load it, if someone is logged in + */ +if (isset($_SESSION['USER'])) { + include APPLICATION_HOME.'/access_control.inc'; +} diff --git a/html/deeds/addDeed.php b/html/deeds/addDeed.php new file mode 100644 index 0000000..06a403c --- /dev/null +++ b/html/deeds/addDeed.php @@ -0,0 +1,34 @@ + + */ + +verifyUser('Administrator'); +if (!userIsAllowed('Deeds')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL.'/deeds'); + exit(); +} + +if (isset($_POST['deed'])) { + $deed = new Deed(); + foreach ($_POST['deed'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $deed->$set($value); + } + + try { + $deed->save(); + header('Location: '.BASE_URL.'/deeds'); + exit(); + } + catch(Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->blocks[] = new Block('deeds/addDeedForm.inc'); +echo $template->render(); \ No newline at end of file diff --git a/html/deeds/home.php b/html/deeds/home.php new file mode 100644 index 0000000..9715e5d --- /dev/null +++ b/html/deeds/home.php @@ -0,0 +1,13 @@ + + */ + +$deedList = new DeedList(); +$deedList->find(); + +$template = new Template(); +$template->blocks[] = new Block('deeds/deedList.inc',array('deedList'=>$deedList)); +echo $template->render(); \ No newline at end of file diff --git a/html/deeds/updateDeed.php b/html/deeds/updateDeed.php new file mode 100644 index 0000000..afb75dd --- /dev/null +++ b/html/deeds/updateDeed.php @@ -0,0 +1,33 @@ + + */ + +if (!userIsAllowed('Deeds')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL.'/deeds'); + exit(); +} + +$deed = new Deed($_REQUEST['deed_id']); +if (isset($_POST['deed'])) { + foreach ($_POST['deed'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $deed->$set($value); + } + + try { + $deed->save(); + header('Location: '.BASE_URL.'/deeds'); + exit(); + } + catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->blocks[] = new Block('deeds/updateDeedForm.inc',array('deed'=>$deed)); +echo $template->render(); \ No newline at end of file diff --git a/html/home.php b/html/home.php new file mode 100644 index 0000000..8f9e1c1 --- /dev/null +++ b/html/home.php @@ -0,0 +1,8 @@ + + */ +$template = new Template(); +echo $template->render(); diff --git a/html/internments/addInternment.php b/html/internments/addInternment.php new file mode 100644 index 0000000..819596a --- /dev/null +++ b/html/internments/addInternment.php @@ -0,0 +1,32 @@ + + */ +if (!userIsAllowed('Internments')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL.'/internments'); + exit(); +} + +if (isset($_POST['internment'])) { + $internment = new Internment(); + foreach ($_POST['internment'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $internment->$set($value); + } + + try { + $internment->save(); + header('Location: '.BASE_URL.'/internments'); + exit(); + } + catch(Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->blocks[] = new Block('internments/addInternmentForm.inc'); +echo $template->render(); \ No newline at end of file diff --git a/html/internments/home.php b/html/internments/home.php new file mode 100644 index 0000000..adc4b73 --- /dev/null +++ b/html/internments/home.php @@ -0,0 +1,14 @@ + + */ + +$internmentList = new InternmentList(); +$internmentList->find(); + +$template = new Template(); +$template->blocks[] = new Block('internments/internmentList.inc', + array('internmentList'=>$internmentList)); +echo $template->render(); \ No newline at end of file diff --git a/html/internments/updateInternment.php b/html/internments/updateInternment.php new file mode 100644 index 0000000..732eeef --- /dev/null +++ b/html/internments/updateInternment.php @@ -0,0 +1,34 @@ + + */ + +if (!userIsAllowed('Internments')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL.'/internments'); + exit(); +} + +$internment = new Internment($_REQUEST['internment_id']); +if (isset($_POST['internment'])) { + foreach ($_POST['internment'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $internment->$set($value); + } + + try { + $internment->save(); + header('Location: '.BASE_URL.'/internments'); + exit(); + } + catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->blocks[] = new Block('internments/updateInternmentForm.inc', + array('internment'=>$internment)); +echo $template->render(); \ No newline at end of file diff --git a/html/js/functions.js b/html/js/functions.js new file mode 100644 index 0000000..11c92df --- /dev/null +++ b/html/js/functions.js @@ -0,0 +1,163 @@ +/** + * @copyright Copyright (C) 2006-2008 City of Bloomington, Indiana. All rights reserved. + * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt + * @author Cliff Ingham + */ +var FRAMEWORK = {}; + +/* A handy function for doing pop-up confirmations when deleting something */ +FRAMEWORK.deleteConfirmation = function (url) +{ + if (confirm("Are you really sure you want to delete this?\n\nOnce deleted it will be gone forever.")) + { + document.location.href = url; + return true; + } + else { return false; } +}; + +/** + * Used for navigating between multiple forms that contain information + * for one object, and are displayed as tabs. Each of the forms must have + * a hidden field for the tab and action, each with the appropriate id. + * Also each of the forms must use the same id in the form tag + * @param id form The id of the form tag + * @param string tab The name of the next tab to display + * @param string action The function to call in the controller for the forms + */ +FRAMEWORK.processTabbedForm = function (form,tab,action) +{ + if (action === 'save') { document.getElementById('continue').value = 'false'; } + document.getElementById('tab').value = tab; + document.getElementById('action').value = action; + document.getElementById(form).submit(); +}; + +/* The following function creates an XMLHttpRequest object useful for doing AJAX stuff */ +FRAMEWORK.getXMLHttpRequestObject = function () +{ + var request; + var browser = navigator.appName; + + if (browser === "Microsoft Internet Explorer") { request = new ActiveXObject("Microsoft.XMLHTTP"); } + else { request = new XMLHttpRequest(); } + + return request; +}; + +FRAMEWORK.getFormValues = function (form) +{ + var params = ""; + + for(var i=0; i maxNumChars) + { + field.value = field.value.substring(0,maxNumChars); + } +} + +/** + * A Date Picker built off the YUI Calendar + * This requires the YUI Toolkit + * @param element The form input to put the chosen date + */ +FRAMEWORK.calendarInit = true; +FRAMEWORK.popupCalendar = function (element) +{ + if (!document.getElementById("popupDatePicker")) + { + var div = document.createElement("div"); + div.setAttribute("id","popupDatePicker"); + div.setAttribute("class","yui-skin-sam"); + element.form.appendChild(div); + } + + FRAMEWORK.dateField = element; + + if (FRAMEWORK.calendarInit) + { + FRAMEWORK.calendarInit = false; + FRAMEWORK.popupDatePicker = new YAHOO.widget.Calendar("popupDatePicker",{"close":true}); + FRAMEWORK.popupDatePicker.selectEvent.subscribe(FRAMEWORK.dateSelectionHandler,FRAMEWORK.popupDatePicker,true); + } + else + { + if (element.value != "") + { + FRAMEWORK.popupDatePicker.select(element.value); + dates = FRAMEWORK.popupDatePicker.getSelectedDates(); + if (dates.length > 0) + { + date = dates[0]; + FRAMEWORK.popupDatePicker.cfg.setProperty("pagedate",(date.getMonth()+1 + "/" + date.getFullYear())); + } + else { alert("Invalid date"); } + } + } + + var xy = YAHOO.util.Dom.getXY(element); + xy[0] += 20; + xy[1] += 20; + YAHOO.util.Dom.setXY ("popupDatePicker", xy, false); + + + FRAMEWORK.popupDatePicker.render(); + FRAMEWORK.popupDatePicker.show(); +} + +FRAMEWORK.dateSelectionHandler = function (type,args,obj) +{ + FRAMEWORK.dateField.value = args[0][0][1] + "/" + args[0][0][2] + "/" + args[0][0][0]; + FRAMEWORK.popupDatePicker.hide(); +} + +/** + * Makes sure dates are correct. Returns false on dates like Feb 30 + * The months are zero based, so January is 0 and December is 11 + * + * @param int month (0=Jan, 11=Dec) + * @param int day + * @param int year + */ +FRAMEWORK.validateDate = function (month,day,year) +{ + var date = new Date(year,month,day); + + if (year != date.getFullYear()) { + alert('Year is not valid!'); + return false; + } + if (month != date.getMonth()) { + alert('Month is not valid!'); + return false; + } + if(day != date.getDate()) { + alert('Day is not valid!'); + return false; + } + return true; +}; diff --git a/html/js/noJavascript.php b/html/js/noJavascript.php new file mode 100644 index 0000000..ba6c2f5 --- /dev/null +++ b/html/js/noJavascript.php @@ -0,0 +1,9 @@ + + */ +$_SESSION['errorMessages'][] = new Exception('noJavascript'); +$template = new Template(); +echo $template->render(); diff --git a/html/login/home.php b/html/login/home.php new file mode 100644 index 0000000..0ce016c --- /dev/null +++ b/html/login/home.php @@ -0,0 +1,9 @@ + + */ +$template = new Template(); +$template->blocks[] = new Block('loginForm.inc'); +echo $template->render(); diff --git a/html/login/login.php b/html/login/login.php new file mode 100644 index 0000000..5570813 --- /dev/null +++ b/html/login/login.php @@ -0,0 +1,33 @@ + + */ +try { + $user = new User($_POST['username']); + + if ($user->authenticate($_POST['password'])) { + $user->startNewSession(); + } + else { + throw new Exception('wrongPassword'); + } +} +catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + header('Location: '.BASE_URL); + exit(); +} + +// The user has successfully logged in. Redirect them wherever you like +if ($_POST['return_url']) { + header('Location: '.$_POST['return_url']); +} +else { + header('Location: '.BASE_URL); +} diff --git a/html/login/logout.php b/html/login/logout.php new file mode 100644 index 0000000..494b5f1 --- /dev/null +++ b/html/login/logout.php @@ -0,0 +1,9 @@ + + */ +session_destroy(); +header('Location: '.BASE_URL); diff --git a/html/people/addPerson.php b/html/people/addPerson.php new file mode 100644 index 0000000..4c6a84d --- /dev/null +++ b/html/people/addPerson.php @@ -0,0 +1,33 @@ + + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +if (isset($_POST['person'])) { + $person = new Person(); + foreach ($_POST['person'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $person->$set($value); + } + + try { + $person->save(); + header('Location: '.BASE_URL.'/people'); + exit(); + } + catch(Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->title = 'Add a person'; +$template->blocks[] = new Block('people/addPersonForm.inc'); +echo $template->render(); diff --git a/html/people/home.php b/html/people/home.php new file mode 100644 index 0000000..6c06cfd --- /dev/null +++ b/html/people/home.php @@ -0,0 +1,19 @@ + + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +$personList = new PersonList(); +$personList->find(); + +$template = new Template(); +$template->title = 'People'; +$template->blocks[] = new Block('people/personList.inc',array('personList'=>$personList)); +echo $template->render(); diff --git a/html/people/updatePerson.php b/html/people/updatePerson.php new file mode 100644 index 0000000..b90d7a3 --- /dev/null +++ b/html/people/updatePerson.php @@ -0,0 +1,35 @@ + + * @param Request person_id + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +$person = new Person($_REQUEST['person_id']); + +if (isset($_POST['person'])) { + foreach ($_POST['person'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $person->$set($value); + } + + try { + $person->save(); + header('Location: '.BASE_URL.'/people'); + exit(); + } + catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->title = 'Update a person'; +$template->blocks[] = new Block('people/updatePersonForm.inc',array('person'=>$person)); +echo $template->render(); diff --git a/html/people/viewPerson.php b/html/people/viewPerson.php new file mode 100644 index 0000000..d5c9be6 --- /dev/null +++ b/html/people/viewPerson.php @@ -0,0 +1,13 @@ + + * @param GET person_id + */ +$person = new Person($_GET['person_id']); + +$template = new Template(); +$template->title = $person->getFullname(); +$template->blocks[] = new Block('people/personInfo.inc',array('person'=>$person)); +echo $template->render(); diff --git a/html/skins/local/layouts/full-width.css b/html/skins/local/layouts/full-width.css new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/html/skins/local/layouts/full-width.css @@ -0,0 +1 @@ + diff --git a/html/skins/local/layouts/three-column.css b/html/skins/local/layouts/three-column.css new file mode 100644 index 0000000..4382b7f --- /dev/null +++ b/html/skins/local/layouts/three-column.css @@ -0,0 +1,6 @@ +#panel-container { overflow:auto; } +#panel-one { float:left; width:180px; } +#panel-two { float:right; width:180px; } +#content-panel { margin-left: 190px; margin-right: 190px; } + +#footer { clear:both; } diff --git a/html/skins/local/layouts/two-column.css b/html/skins/local/layouts/two-column.css new file mode 100644 index 0000000..07cec6c --- /dev/null +++ b/html/skins/local/layouts/two-column.css @@ -0,0 +1,5 @@ +#panel-container { overflow:auto; } +#panel-one { float:left; width:176px; } +#content-panel { margin-left: 178px; } + +#footer { clear:both; } diff --git a/html/skins/local/screen.css b/html/skins/local/screen.css new file mode 100644 index 0000000..e69de29 diff --git a/html/users/addUser.php b/html/users/addUser.php new file mode 100644 index 0000000..51049da --- /dev/null +++ b/html/users/addUser.php @@ -0,0 +1,73 @@ + + * @param GET person_id + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +if (isset($_REQUEST['person_id'])) { + try { + $person = new Person($_REQUEST['person_id']); + } + catch (Exception $e) { + } +} + +if (isset($_POST['user'])) { + + $user = new User(); + foreach ($_POST['user'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $user->$set($value); + } + + if (isset($person)) { + $user->setPerson_id($person->getId()); + } + else { + // Load their information from LDAP + // Delete this statement if you're not using LDAP + if ($user->getAuthenticationMethod() == 'LDAP') { + try { + $ldap = new LDAPEntry($user->getUsername()); + try { + $person = new Person($ldap->getEmail()); + } + catch (Exception $e) { + $person = new Person(); + $person->setFirstname($ldap->getFirstname()); + $person->setLastname($ldap->getLastname()); + $person->setEmail($ldap->getEmail()); + $person->save(); + } + $user->setPerson($person); + } + catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + } + } + } + + try { + $user->save(); + header('Location: '.BASE_URL.'/users'); + exit(); + } + catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->title = 'Create a user account'; +$template->blocks[] = new Block('users/addUserForm.inc'); +if (isset($person)) { + $template->blocks[] = new Block('people/personInfo.inc',array('person'=>$person)); +} +echo $template->render(); diff --git a/html/users/deleteUser.php b/html/users/deleteUser.php new file mode 100644 index 0000000..ed42225 --- /dev/null +++ b/html/users/deleteUser.php @@ -0,0 +1,17 @@ + + * @param GET user_id + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +$user = new User($_GET['user_id']); +$user->delete(); + +header('Location: '.BASE_URL.'/users'); diff --git a/html/users/home.php b/html/users/home.php new file mode 100644 index 0000000..dd361d4 --- /dev/null +++ b/html/users/home.php @@ -0,0 +1,20 @@ + + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +$template = new Template(); +$template->title = 'User accounts'; + +$userList = new UserList(); +$userList->find(); +$template->blocks[] = new Block('users/userList.inc',array('userList'=>$userList)); + +echo $template->render(); diff --git a/html/users/updateUser.php b/html/users/updateUser.php new file mode 100644 index 0000000..5c212d3 --- /dev/null +++ b/html/users/updateUser.php @@ -0,0 +1,35 @@ + + * @param REQUEST user_id + */ +if (!userIsAllowed('Users')) { + $_SESSION['errorMessages'][] = new Exception('noAccessAllowed'); + header('Location: '.BASE_URL); + exit(); +} + +$user = new User($_REQUEST['user_id']); + +if (isset($_POST['user'])) { + foreach ($_POST['user'] as $field=>$value) { + $set = 'set'.ucfirst($field); + $user->$set($value); + } + + try { + $user->save(); + header('Location: '.BASE_URL.'/users'); + exit(); + } + catch (Exception $e) { + $_SESSION['errorMessages'][] = $e; + } +} + +$template = new Template(); +$template->blocks[] = new Block('users/updateUserForm.inc',array('user'=>$user)); +$template->blocks[] = new BlocK('people/personInfo.inc',array('person'=>$user->getPerson())); +echo $template->render(); diff --git a/installation.txt b/installation.txt new file mode 100644 index 0000000..855e86c --- /dev/null +++ b/installation.txt @@ -0,0 +1,139 @@ +--------------------------------------------------------------------- +Contents +--------------------------------------------------------------------- + About + + Copyright + + Requirements + + Installation + +--------------------------------------------------------------------- +About +--------------------------------------------------------------------- + This scaffolding is really just a template for our web +applications. It provides us the basics to get a web application up +and running very quickly. It also means our applications will be +relatively uniform and easier to extend and maintain down the road. + While not fully MVC, it has borrowed ideas from Rails +and Struts. It has developed over time, and will continue to develop +as we come across better solutions to problems run into while creating +new applications. + +--------------------------------------------------------------------- +Copyright +--------------------------------------------------------------------- + This scaffolding is written and copyrighted by the +City of Bloomington, IN. It is being released as free software; +you can redistribute it and/or modify it under the terms of the +GNU Affero General Public License as published by the Free Software Foundation; +either version 3 of the License, or (at your option) any later version. + + This scaffolding includes code generators to create classes to +work with your database. While the generators fall under the GPL, +any code you generate with them belongs to you. However, we highly +encourage you to uphold the values of Free Software and release your +application as Free Software as well. + + In any case, you own the copyright to any generated code and should +edit the copyright statement in the configuration. This will be the +copyright statement that will be included in all generated code. + + +--------------------------------------------------------------------- +Requirements +--------------------------------------------------------------------- +framework: +This scaffolding requires our framework to be on the server somewhere. +It ships with a working version of framework installed in /libraries. +In the configuration, you can point the application to another copy +of the framework, if you like. If you make changes to the code in +the /libraries/framework, you might consider sending those changes +back to us, so we can improve this scaffolding. + +ZendFramework: +Database interaction for this application is done using Zend_Db. You must +have downloaded a copy of the ZendFramework for this to work. In the +configuration, you will point the application to wherever you've installed +your copy of the ZendFramework. The ZendFramework is available under a +BSD license at: +http://framework.zend.com/ + +Apache: +This application was written assuming you have control over your own web +server. If you are on a hosted system, you may need to make changes to +the code to accomodate your server. + +All webserver instructions assume the Apache webserver. It is certainly +possible to set this using a different web server. However, we don't have +any experience with other webservers and cannot provide information on +their configuration. + +All include files have been kept out of the web directory, and a configuration +file has been included. You will need to make sure that every .php script +in the HTML directory includes configuration.inc. If you control your own +webserver, you can add a command to your httpd.conf. + +PHP: +PHP must be compiled with support for: + PDO + MySQL + LDAP + + Actually, you can substitute any database support for MySQL, as our +framework uses PDO for database interaction. However, the initial SQL +schema provided is specific to MySQL. The schema would need to be modified +for other databases. + +MySQL: + MySQL should have support for InnoDB. Foreign keys are written into +the database load scripts. While MyISAM tables will just ignore them, +you database and your application will be that much more robust with InnoDB +support. + +LDAP: + LDAP is only used for doing LDAP authentication for users. +If you're not going to do any LDAP authentication, you can delete or +comment out the LDAP stuff. + +--------------------------------------------------------------------- +Installation +--------------------------------------------------------------------- + This scaffolding is essentially a working web application that is +ready to be extended. It has authentication and user management +already built. + + The best way is to have PHP auto_prepend the configuration.inc. +If this is the only web application on your apache server, you can just point +your apache's web directory to the html directory inside the application and +edit the auto_prepend in your php.ini file. + + For us, we're running multiple applications, and have a seperate entry in +our Apache config for each one. This does essentially the same thing. + +Add to httpd.conf: + +Alias /application_name "/path/to/application_name/html" + + Options FollowSymLinks + AllowOverride None + Order allow,deny + Allow from all + + php_value auto_prepend_file /path/to/application_name/configuration.inc + + + + If you're running in a shared hosting environment, you cannot do Alias or +Directory commands. However the html can be moved into your web directory. +To make sure the configuration file gets loaded, create an htaccess file in +this application's html directory with the php_value line from above. + + If you cannot edit your httpd.conf or use htaccess files, you will need to +add an include() command to the top of every last PHP script in the html directory. + +--------------------------------------------------------------------- +Configuration +--------------------------------------------------------------------- + Edit configuration.inc with appropriate values for your installation. diff --git a/libraries/framework/blocks/html/pageNavigation.inc b/libraries/framework/blocks/html/pageNavigation.inc new file mode 100644 index 0000000..5555a8f --- /dev/null +++ b/libraries/framework/blocks/html/pageNavigation.inc @@ -0,0 +1,40 @@ + + * @param Zend_Paginator $this->pages + */ +if ($this->pages->pageCount > 1) { + $url = new URL($_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']); + $url->purgeEmptyParameters(); + + echo ''; +} diff --git a/libraries/framework/classes/Block.php b/libraries/framework/classes/Block.php new file mode 100644 index 0000000..ab4974a --- /dev/null +++ b/libraries/framework/classes/Block.php @@ -0,0 +1,62 @@ + + */ +class Block extends View +{ + private $file; + + /** + * Establishes the block script to use for rendering + * + * Blocks are files contained in the base path of: + * APPLICATION_HOME/blocks/$outpuform + * + * @param string $file + * @param array $vars An associative array of variables to set + */ + public function __construct($file,array $vars=null) + { + $this->file = $file; + if (count($vars)) { + foreach ($vars as $name=>$value) { + $this->vars[$name] = $value; + } + } + } + + /** + * Includes the block script and returns the output as a string + * + * @param string $outputFormat + * @return string + */ + public function render($outputFormat='html') + { + $block = "/blocks/$outputFormat/{$this->file}"; + + if (file_exists(APPLICATION_HOME.$block)) { + ob_start(); + include APPLICATION_HOME.$block; + return ob_get_clean(); + } + elseif (file_exists(FRAMEWORK.$block)) { + ob_start(); + include FRAMEWORK.$block; + return ob_get_clean(); + } + + throw new Exception('unknownBlock'); + } +} diff --git a/libraries/framework/classes/Database.php b/libraries/framework/classes/Database.php new file mode 100644 index 0000000..9e775cc --- /dev/null +++ b/libraries/framework/classes/Database.php @@ -0,0 +1,59 @@ + + */ +class Database +{ + private static $connection; + + /** + * @param boolean $reconnect If true, drops the connection and reconnects + * @return resource + */ + public static function getConnection($reconnect=false) + { + if ($reconnect) { + self::$connection=null; + } + if (!self::$connection) { + try { + $parameters = array('host'=>DB_HOST, + 'username'=>DB_USER, + 'password'=>DB_PASS, + 'dbname'=>DB_NAME, + 'options'=>array(Zend_Db::AUTO_QUOTE_IDENTIFIERS=>false)); + self::$connection = Zend_Db::factory(DB_ADAPTER,$parameters); + self::$connection->getConnection(); + } + catch (Exception $e) { + die($e->getMessage()); + } + } + return self::$connection; + } + + /** + * Returns the type of database that's being used (mysql, oracle, etc.) + * + * @return string + */ + public static function getType() + { + switch (strtolower(DB_ADAPTER)) { + case 'pdo_mysql': + case 'mysqli': + return 'mysql'; + break; + + case 'pdo_oci': + case 'oci8': + return 'oracle'; + break; + } + + } +} diff --git a/libraries/framework/classes/Date.php b/libraries/framework/classes/Date.php new file mode 100644 index 0000000..d005539 --- /dev/null +++ b/libraries/framework/classes/Date.php @@ -0,0 +1,46 @@ + + */ +class Date extends DateTime +{ + /** + * Handles array dates passed in the constructor. + * + * Wrapper for DateTime constructor. If arrays are passed, they will be + * handled here. Anything else will be passed to the DateTime constructor. + * Arrays should be in the form of PHP's getdate() array + * + * @param array $date + */ + public function __construct($date=null) + { + if (is_array($date)) { + if ($date['year'] && $date['mon'] && $date['mday']) { + $dateString = "$date[year]-$date[mon]-$date[mday]"; + + if (isset($date['hours']) || isset($date['minutes']) || isset($date['seconds'])) { + $time = (isset($date['hours']) && $date['hours']) ? "$date[hours]:" : '00:'; + $time.= (isset($date['minutes']) && $date['minutes']) ? "$date[minutes]:" : '00:'; + $time.= (isset($date['seconds']) && $date['seconds']) ? $date['seconds'] : '00'; + + $dateString.= " $time"; + } + $date = $dateString; + } + } + if (is_int($date)) { + $date = date('Y-m-d',$date); + } + if (!$date instanceof DateTime) { + parent::__construct($date); + } + } + + public function __toString() + { + return $this->format('n/j/Y'); + } +} \ No newline at end of file diff --git a/libraries/framework/classes/ExternalAuthentication.php b/libraries/framework/classes/ExternalAuthentication.php new file mode 100644 index 0000000..50828e9 --- /dev/null +++ b/libraries/framework/classes/ExternalAuthentication.php @@ -0,0 +1,12 @@ + + */ + +interface ExternalAuthentication +{ + public static function authenticate($username,$password); + public static function savePassword($username,$password); +} \ No newline at end of file diff --git a/libraries/framework/classes/Inflector.php b/libraries/framework/classes/Inflector.php new file mode 100644 index 0000000..b14d4bf --- /dev/null +++ b/libraries/framework/classes/Inflector.php @@ -0,0 +1,332 @@ + + * Copyright 2005-2008, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * + * @filesource + * @copyright Copyright 2005-2008, Cake Software Foundation, Inc. + * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project + * @package cake + * @subpackage cake.cake.libs + * @since CakePHP(tm) v 0.2.9 + * @version $Revision: 6311 $ + * @modifiedby $LastChangedBy: phpnut $ + * @lastmodified $Date: 2008-01-02 00:33:52 -0600 (Wed, 02 Jan 2008) $ + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + * + * Modified to work in City of Bloomington's Framework + * Relicensed under GPL + * Redistributions must retain all copyright notices + * @copyright 2006-2008 City of Bloomington, Indiana + * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt + */ +class Inflector +{ + /** + * Return $word in plural form. + * + * @param string $word Word in singular + * @return string Word in plural + */ + public static function pluralize($word) { + $corePluralRules = array('/(s)tatus$/i' => '\1\2tatuses', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(buffal|tomat)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cri|test)is$/i' => '\1es', + '/s$/' => 's', + '/$/' => 's',); + + $coreUninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'Amoyese', + 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', + 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk', + 'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese', + 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news', + 'nexus', 'Niasese', 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings', + 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears', + 'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', + 'whiting', 'wildebeest', 'Yengeese',); + + $coreIrregularPlural = array('atlas' => 'atlases', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'child' => 'children', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hoof' => 'hoofs', + 'loaf' => 'loaves', + 'man' => 'men', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs',); + + $pluralRules = $corePluralRules; + $uninflected = $coreUninflectedPlural; + $irregular = $coreIrregularPlural; + + if (file_exists(FRAMEWORK.'/includes/inflections.inc')) { + include(FRAMEWORK.'/includes/inflections.inc'); + $pluralRules = array_merge($pluralRules, $corePluralRules); + $uninflected = array_merge($uninflectedPlural, $coreUninflectedPlural); + $irregular = array_merge($irregularPlural, $coreIrregularPlural); + } + $regexUninflected = self::enclose(join( '|', $uninflected)); + $regexIrregular = self::enclose(join( '|', array_keys($irregular))); + + if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) { + return $word; + } + + if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) { + return $regs[1] . $irregular[strtolower($regs[2])]; + } + + foreach ($pluralRules as $rule => $replacement) { + if (preg_match($rule, $word)) { + $replace = preg_replace($rule, $replacement, $word); + return $replace; + } + } + return $word; + } + /** + * Return $word in singular form. + * + * @param string $word Word in plural + * @return string Word in singular + */ + public static function singularize($word) { + $coreSingularRules = array('/(s)tatuses$/i' => '\1\2tatus', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/(cris|ax|test)es$/i' => '\1is', + '/(shoe)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/uses$/' => 'us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^f])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(n)ews$/i' => '\1\2ews', + '/^(.*us)$/' => '\\1', + '/s$/i' => ''); + + $coreUninflectedSingular = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss', 'Amoyese', + 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', + 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk', + 'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese', + 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news', + 'nexus', 'Niasese', 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings', + 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears', + 'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', + 'whiting', 'wildebeest', 'Yengeese',); + + $coreIrregularSingular = array('atlases' => 'atlas', + 'beefs' => 'beef', + 'brothers' => 'brother', + 'children' => 'child', + 'corpuses' => 'corpus', + 'cows' => 'cow', + 'ganglions' => 'ganglion', + 'genies' => 'genie', + 'genera' => 'genus', + 'graffiti' => 'graffito', + 'hoofs' => 'hoof', + 'loaves' => 'loaf', + 'men' => 'man', + 'monies' => 'money', + 'mongooses' => 'mongoose', + 'moves' => 'move', + 'mythoi' => 'mythos', + 'numina' => 'numen', + 'occiputs' => 'occiput', + 'octopuses' => 'octopus', + 'opuses' => 'opus', + 'oxen' => 'ox', + 'penises' => 'penis', + 'people' => 'person', + 'sexes' => 'sex', + 'soliloquies' => 'soliloquy', + 'testes' => 'testis', + 'trilbys' => 'trilby', + 'turfs' => 'turf',); + + $singularRules = $coreSingularRules; + $uninflected = $coreUninflectedSingular; + $irregular = $coreIrregularSingular; + + if (file_exists(FRAMEWORK.'/includes/inflections.inc')) { + include(FRAMEWORK.'/includes/inflections.inc'); + $singularRules = array_merge($singularRules, $coreSingularRules); + $uninflected = array_merge($uninflectedSingular, $coreUninflectedSingular); + $irregular = array_merge($irregularSingular, $coreIrregularSingular); + } + $regexUninflected = self::enclose(join( '|', $uninflected)); + $regexIrregular = self::enclose(join( '|', array_keys($irregular))); + + if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) { + return $word; + } + + if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) { + return $regs[1] . $irregular[strtolower($regs[2])]; + } + + foreach ($singularRules as $rule => $replacement) { + if (preg_match($rule, $word)) { + $replace = preg_replace($rule, $replacement, $word); + return $replace; + } + } + return $word; + } +/** + * Returns given $lower_case_and_underscored_word as a camelCased word. + * + * @param string $lower_case_and_underscored_word Word to camelize + * @return string Camelized word. likeThis. + */ + public static function camelize($lowerCaseAndUnderscoredWord) { + $replace = str_replace(" ", "", ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord))); + return $replace; + } +/** + * Returns an underscore-syntaxed ($like_this_dear_reader) version of the $camel_cased_word. + * + * @param string $camel_cased_word Camel-cased word to be "underscorized" + * @return string Underscore-syntaxed version of the $camel_cased_word + */ + public static function underscore($camelCasedWord) { + $replace = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord)); + return $replace; + } +/** + * Returns a human-readable string from $lower_case_and_underscored_word, + * by replacing underscores with a space, and by upper-casing the initial characters. + * + * @param string $lower_case_and_underscored_word String to be made more readable + * @return string Human-readable string + */ + public static function humanize($lowerCaseAndUnderscoredWord) { + $replace = ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord)); + return $replace; + } +/** + * Returns corresponding table name for given $class_name. ("posts" for the model class "Post"). + * + * @param string $class_name Name of class to get database table name for + * @return string Name of the database table for given class + */ + public static function tableize($className) { + $replace = self::pluralize(self::underscore($className)); + return $replace; + } +/** + * Returns Cake model class name ("Post" for the database table "posts".) for given database table. + * + * @param string $tableName Name of database table to get class name for + * @return string + */ + public static function classify($tableName) { + $replace = self::camelize(self::singularize($tableName)); + return $replace; + } + +/** + * Returns camelBacked version of a string. + * + * @param string $string + * @return string + * @access public + * @static + */ + public static function variable($string) { + $string = self::camelize(self::underscore($string)); + $replace = strtolower(substr($string, 0, 1)); + $variable = preg_replace('/\\w/', $replace, $string, 1); + return $variable; + } +/** + * Returns a string with all spaces converted to $replacement and non word characters removed. + * + * @param string $string + * @param string $replacement + * @return string + * @access public + * @static + */ + public static function slug($string, $replacement = '_') { + $string = preg_replace(array('/[^\w\s]/', '/\\s+/') , array(' ', $replacement), $string); + return $string; + } + +/** + * Enclose a string for preg matching. + * + * @param string $string String to enclose + * @return string Enclosed string + */ + public static function enclose($string) { + return '(?:' . $string . ')'; + } +} +?> diff --git a/libraries/framework/classes/LDAP.php b/libraries/framework/classes/LDAP.php new file mode 100644 index 0000000..42d4ac5 --- /dev/null +++ b/libraries/framework/classes/LDAP.php @@ -0,0 +1,87 @@ + + */ +class LDAP implements ExternalAuthentication +{ + /** + * @param string $username + * @param string $password + * @throws Exception + */ + public static function authenticate($username,$password) + { + $connection = ldap_connect(LDAP_SERVER) or die("Couldn't connect to LDAP"); + ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_bind($connection); + + $result = ldap_search($connection,LDAP_DN,LDAP_USERNAME_ATTRIBUTE."=$username"); + if (ldap_count_entries($connection,$result)) { + $entries = ldap_get_entries($connection, $result); + + if (preg_match("/^\{crypt\}(.+)/i",$entries[0][LDAP_PASSWORD_ATTRIBUTE][0],$matches)) { + $ldapPassword = $matches[1]; + $salt = substr($ldapPassword,0,2); + + $encryptedPassword = crypt($password,$salt); + if ($encryptedPassword === $ldapPassword) { + return true; + } + else { + throw new Exception('wrongPassword'); + } + } + else { + throw new Exception("passwordIsCorrupted"); + } + } + else { + throw new Exception("unknownUser"); + } + } + + /** + * Saves a user's password to the LDAP server + * + * @param string $username + * @param string $password + */ + public static function savePassword($username,$password) + { + $connection = ldap_connect(LDAP_SERVER); + ldap_set_option($connection,LDAP_OPT_PROTOCOL_VERSION,3); + ldap_bind($connection, + LDAP_USERNAME_ATTRIBUTE."=".LDAP_ADMIN_USER.",o=".LDAP_DOMAIN, + LDAP_ADMIN_PASS) or die(ldap_error($connection)); + + $result = ldap_search($connection,LDAP_DN,LDAP_USERNAME_ATTRIBUTE."=$username"); + $entries = ldap_get_entries($connection, $result); + + $dn = LDAP_USERNAME_ATTRIBUTE."=$username,ou=people,o=".LDAP_DOMAIN; + if ($this->getPassword()) { + $salt = substr(md5(time()),0,2); + $encryptedPassword = "{CRYPT}".crypt($password,$salt); + + $password = array(LDAP_PASSWORD_ATTRIBUTE=>$encryptedPassword); + + if (isset($entries[0][LDAP_PASSWORD_ATTRIBUTE])) { + // Modify + ldap_mod_replace($connection,$dn,$password) + or die(print_r($password).ldap_error($connection)); + } + else { + // Add + ldap_mod_add($connection,$dn,$password) + or die(print_r($password).ldap_error($connection)); + } + } + else { + // Delete + $password = array(); + ldap_mod_del($connection,$dn,$password) + or die(print_r($password).ldap_error($connection)); + } + } +} diff --git a/libraries/framework/classes/LDAPEntry.php b/libraries/framework/classes/LDAPEntry.php new file mode 100644 index 0000000..f544aad --- /dev/null +++ b/libraries/framework/classes/LDAPEntry.php @@ -0,0 +1,526 @@ + + */ +class LDAPEntry +{ + private static $connection; + + private $ou; + + private $uid; + private $userPassword; + + private $givenName; + private $sn; + private $cn; + private $displayName; + + private $businessCategory; + private $departmentNumber; + private $physicalDeliveryOfficeName; + private $title; + + private $mail; + private $telephoneNumber; + private $preferredTelephoneNumber; + private $webTelephoneNumber; + private $facsimileTelephoneNumber; + private $homePhone; + private $mobile; + private $dialupAccess; + + private $jpegPhoto; + + private $sambaLMPassword; + private $sambaNTPassword; + private $sambaSID; + + private $objectClasses = array(); + + // Used to keep track of changes we make to this entry. This is because LDAP + // requires us to send seperate modify, add, and delete commands. + private $modifiedAttributes = array(); + private $addedAttributes = array(); + private $deletedAttributes = array(); + + + /** + * Loads an entry from the LDAP server for the given user + * @param string $username + */ + public function __construct($username=null) + { + $this->openConnection(); + + if ($username) { + $result = ldap_search(LDAPEntry::$connection,LDAP_DN, + LDAP_USERNAME_ATTRIBUTE."=$username"); + if (ldap_count_entries(LDAPEntry::$connection,$result)) { + $entries = ldap_get_entries(LDAPEntry::$connection, $result); + $this->uid = $username; + if (isset($entries[0]['ou'])) { + $this->ou = $entries[0]['ou'][0]; + } + if (isset($entries[0]['givenname'])) { + $this->givenName = $entries[0]['givenname'][0]; + } + if (isset($entries[0]['sn'])) { + $this->sn = $entries[0]['sn'][0]; + } + if (isset($entries[0]['cn'])) { + $this->cn = $entries[0]['cn'][0]; + } + if (isset($entries[0]['displayname'])) { + $this->displayName = $entries[0]['displayname'][0]; + } + if (isset($entries[0]['businesscategory'])) { + $this->businessCategory = $entries[0]['businesscategory'][0]; + } + if (isset($entries[0]['departmentnumber'])) { + $this->departmentNumber = $entries[0]['departmentnumber'][0]; + } + if (isset($entries[0]['physicaldeliveryofficename'])) { + $this->physicalDeliveryOfficeName = $entries[0]['physicaldeliveryofficename'][0]; + } + if (isset($entries[0]['title'])) { + $this->title = $entries[0]['title'][0]; + } + if (isset($entries[0]['mail'])) { + $this->mail = $entries[0]['mail'][0]; + } + if (isset($entries[0]['telephonenumber'])) { + $this->telephoneNumber = $entries[0]['telephonenumber'][0]; + } + if (isset($entries[0]['preferredtelephonenumber'])) { + $this->preferredTelephoneNumber = $entries[0]['preferredtelephonenumber'][0]; + } + if (isset($entries[0]['webtelephonenumber'])) { + $this->webTelephoneNumber = $entries[0]['webtelephonenumber'][0]; + } + if (isset($entries[0]['facsimiletelephonenumber'])) { + $this->facsimileTelephoneNumber = $entries[0]['facsimiletelephonenumber'][0]; + } + if (isset($entries[0]['homephone'])) { + $this->homePhone = $entries[0]['homephone'][0]; + } + if (isset($entries[0]['mobile'])) { + $this->mobile = $entries[0]['mobile'][0]; + } + if (isset($entries[0]['dialupaccess'])) { + $this->dialupAccess = $entries[0]['dialupaccess'][0]; + } + if (isset($entries[0]['objectclass'])) { + $this->objectClasses = $entries[0]['objectclass']; + } + if (isset($entries[0]['jpegphoto'])) { + $photo = ldap_get_values_len(LDAPEntry::$connection, + ldap_first_entry(LDAPEntry::$connection,$result), + 'jpegphoto'); + $this->jpegPhoto = $photo[0]; + } + } + else { + throw new Exception("ldap/unknownUser"); + } + } + } + + /** + * Creates the connection to the LDAP server + */ + private function openConnection() + { + if (!LDAPEntry::$connection) { + if (LDAPEntry::$connection = ldap_connect(LDAP_SERVER)) { + ldap_set_option(LDAPEntry::$connection,LDAP_OPT_PROTOCOL_VERSION,3); + if (LDAP_ADMIN_USER) { + if (!ldap_bind(LDAPEntry::$connection, + LDAP_USERNAME_ATTRIBUTE."=".LDAP_ADMIN_USER.",o=".LDAP_DOMAIN, + LDAP_ADMIN_PASS)) { + throw new Exception(ldap_error(LDAPEntry::$connection)); + } + } + else { + if (!ldap_bind(LDAPEntry::$connection)) { + throw new Exception(ldap_error(LDAPEntry::$connection)); + } + } + } + else { + throw new Exception(ldap_error(LDAPEntry::$connection)); + } + } + } + + /** + * Saves any changed information back to the LDAP server + */ + public function save() + { + $dn = "uid={$this->uid},ou=people,o=".LDAP_DOMAIN; + if (count($this->modifiedAttributes)) { + ldap_mod_replace(LDAPEntry::$connection,$dn,$this->modifiedAttributes) + or die(print_r($this->modifiedAttributes).ldap_error(LDAPEntry::$connection)); + } + if (count($this->addedAttributes)) { + ldap_mod_add(LDAPEntry::$connection,$dn,$this->addedAttributes) + or die(print_r($this->addedAttributes).ldap_error(LDAPEntry::$connection)); + } + if (count($this->deletedAttributes)) { + ldap_mod_del(LDAPEntry::$connection,$dn,$this->deletedAttributes) + or die(print_r($this->deletedAttributes).ldap_error(LDAPEntry::$connection)); + } + } + + /** + * Escapes any problematic characters + * @param string $str + */ + private function sanitize($str) + { + $tmp = trim($str); + $tmp = str_replace('\\', '\\\\', $tmp); + $tmp = str_replace('(', '\(', $tmp); + $tmp = str_replace(')', '\)', $tmp); + $tmp = str_replace('*', '\*', $tmp); + return $tmp; + } + + /** + * Keeps track of what properties have been changed + * + * All setters should call this function. Otherwise, we won't + * know what's been changed in order to do the appropriate calls in LDAP + * @param string $property + * @param string $value + */ + private function changeProperty($property,$value) + { + if ($value) { + if ($value != $this->{$property}) { + if ($this->{$property}) { + $this->modifiedAttributes[$property] = $value; + } + else { + $this->addedAttributes[$property] = $value; + } + $this->{$property} = $value; + } + } + else { + if ($this->{$property}) { + $this->{$property} = ''; + $this->deletedAttributes[$property] = array(); + } + } + } + + /** + * @return string + */ + public function getOU() + { + return $this->ou; + } + /** + * @return string + */ + public function getUID() + { + return $this->uid; + } + /** + * @return string + */ + public function getUsername() + { + return $this->uid; + } + /** + * @return string + */ + public function getFirstname() + { + return $this->givenName; + } + /** + * @return string + */ + public function getLastname() + { + return $this->sn; + } + /** + * @return string + */ + public function getCommonName() + { + return $this->cn; + } + /** + * @return string + */ + public function getDisplayName() + { + return $this->displayName; + } + /** + * @return string + */ + public function getBusinessCategory() + { + return $this->businessCategory; + } + /** + * @return string + */ + public function getDepartment() + { + return $this->departmentNumber; + } + /** + * @return string + */ + public function getOffice() + { + return $this->physicalDeliveryOfficeName; + } + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } + /** + * @return string + */ + public function getEmail() + { + return $this->mail; + } + /** + * @return string + */ + public function getPhone() + { + return $this->telephoneNumber; + } + /** + * @return string + */ + public function getPreferredPhone() + { + return $this->preferredTelephoneNumber; + } + /** + * @return string + */ + public function getWebPhone() + { + return $this->webTelephoneNumber; + } + /** + * @return string + */ + public function getFax() + { + return $this->facsimileTelephoneNumber; + } + /** + * @return string + */ + public function getHomePhone() + { + return $this->homePhone; + } + /** + * @return string + */ + public function getCellPhone() + { + return $this->mobile; + } + /** + * @return string + */ + public function getDialup() + { + return $this->dialupAccess; + } + /** + * @return string + */ + public function getSambaLMPassword() + { + return $this->sambaLMPassword; + } + /** + * @return string + */ + public function getSambaNTPassword() + { + return $this->sambaNTPassword; + } + /** + * @return string + */ + public function getSambaSID() + { + return $this->sambaSID; + } + /** + * @return string + */ + public function getObjectClasses() + { + return $this->objectClasses; + } + /** + * @return raw + */ + public function getPhoto() + { + return $this->jpegPhoto; + } + + /** + * @param string $string + */ + public function setUsername($string) + { + $this->changeProperty("uid",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setFirstname($string) + { + $this->changeProperty("givenName",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setLastname($string) + { + $this->changeProperty("sn",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setCommonName($string) + { + $this->changeProperty("cn",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setDisplayName($string) + { + $this->changeProperty("displayName",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setBusinessCategory($string) + { + $this->changeProperty("businessCategory",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setDepartment($string) + { + $this->changeProperty("departmentNumber",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setOffice($string) + { + $this->changeProperty("physicalDeliveryOfficeName",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setTitle($string) + { + $this->changeProperty("title",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setEmail($string) + { + $this->changeProperty("mail",$this->sanitize($string)); + } + /** + * @param string $string + */ + public function setPhone($string) + { + $this->changeProperty("telephoneNumber",preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $string + */ + public function setPreferredPhone($string) + { + $this->changeProperty('preferredTelephoneNumber', + preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $string + */ + public function setWebPhone($string) + { + $this->changeProperty('webTelephoneNumber', + preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $string + */ + public function setFax($string) + { + $this->changeProperty('facsimileTelephoneNumber', + preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $string + */ + public function setHomePhone($string) + { + $this->changeProperty('homePhone',preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $string + */ + public function setCellPhone($string) + { + $this->changeProperty('mobile',preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $string + */ + public function setDialup($string) + { + $this->changeProperty('dialupAccess',preg_replace('/[^0-9ext\-\s]/','',$string)); + } + /** + * @param string $filePath + */ + public function setPhoto($filePath) + { + $this->changeProperty("jpegPhoto",file_get_contents($filePath)); + } +} diff --git a/libraries/framework/classes/Paginator.php b/libraries/framework/classes/Paginator.php new file mode 100644 index 0000000..9b3c199 --- /dev/null +++ b/libraries/framework/classes/Paginator.php @@ -0,0 +1,147 @@ + + */ +class Paginator implements ArrayAccess,SeekableIterator,Countable +{ + private $pageSize; + private $pages = array(); + private $key = 0; + + /** + * @param array $list + * @param int $pageSize + */ + public function __construct($list,$pageSize) + { + $this->pageSize = $pageSize; + $totalPageCount = count($list) / $this->pageSize; + for ($i=0; $i<$totalPageCount; $i++) { + $this->pages[] = $i * $this->pageSize; + } + } + + /** + * Returns the number of elements per page + * @return int + */ + public function getPageSize() + { + return $this->pageSize; + } + /** + * Returns the vary last index number for this paginator + * @return int + */ + public function getLastIndex() + { + return count($this->pages)-1; + } + + // Array Access section + /** + * @param int $offset + * @return boolean + */ + public function offsetExists($offset) + { + return array_key_exists($offset,$this->pages); + } + /** + * Returns the array of elements for a given page + * @param int $offset + * @return array + */ + public function offsetGet($offset) + { + return $this->pages[$offset]; + } + /** + * Unimplemented stub required for SPLIterator interface + * Once created, paginators are read-only + */ + public function offsetSet($offset,$value) + { + // Read-only for now + } + /** + * Unimplemented stub required for SPLIterator interface + * Once created, paginators are read-only + */ + public function offsetUnset($offset) + { + // Read only for now + } + + + // Iterator Interface stuff + /** + * Reset the iterator to the start + */ + public function rewind() + { + $this->key = 0; + } + /** + * Move the pointer to the next element + */ + public function next() + { + $this->key++; + } + /** + * @return int + */ + public function key() + { + return $this->key; + } + /** + * @return boolean + */ + public function valid() + { + return array_key_exists($this->key,$this->pages); + } + /** + * Return the array of element for the current page + * @return array + */ + public function current() + { + return $this->pages[$this->key]; + } + /** + * Go to a specific page + * @param int $index + */ + public function seek($index) + { + if (isset($this->pages[$index])) { + $this->key = $index; + } + else { + throw new OutOfBoundsException('Invalid seek position'); + } + } + /** + * @return Iterator + */ + public function getIterator() + { + return $this; + } + + // Countable interface section + /** + * @return int + */ + public function count() + { + return count($this->pages); + } +} diff --git a/libraries/framework/classes/SystemUser.php b/libraries/framework/classes/SystemUser.php new file mode 100644 index 0000000..61d6d68 --- /dev/null +++ b/libraries/framework/classes/SystemUser.php @@ -0,0 +1,92 @@ + + */ +abstract class SystemUser +{ + abstract public function getId(); + abstract public function getUsername(); + abstract public function getAuthenticationMethod(); + abstract public function getRoles(); + + abstract public function hasRole($roles); + + abstract public function setAuthenticationMethod($method); + abstract public function setRoles($roles); + abstract public function setUsername($username); + + /** + * Passwords are set in clear text. The only times you would want to set a password + * is when you're adding a new password or changing a person's password. + * Either way, it's up to the individual save routines to handle encrypting the new password + * before storing it. Passwords should not be loaded in the constructor - they're + * supposed to be encrypted, so what's the point? + */ + abstract public function setPassword($password); + + /** + * Used to hand authentication off to the application + */ + abstract protected function authenticateDatabase($password); + + /** + * Used to hand password saving off to the application + */ + abstract protected function saveLocalPassword(); + + /** + * Determines which authentication scheme to use for the user and calls the appropriate method + * + * @param string $password + * @return boolean + */ + public function authenticate($password) + { + switch($this->getAuthenticationMethod()) { + case "local": + return $this->authenticateDatabase($password); + break; + + default: + $type = $this->getAuthenticationMethod(); + return call_user_func(array($type,'authenticate'),$this->getUsername(),$password); + } + } + + /** + * Establishes a new Session and loads the default information for the user + */ + public function startNewSession() + { + session_destroy(); + session_start(); + + $_SESSION['USER'] = $this; + $_SESSION['IP_ADDRESS'] = $_SERVER['REMOTE_ADDR']; + } + + /** + * Determines which authentication method is being used, and sends the password to the + * appropriate method + */ + public function savePassword() + { + switch($this->getAuthenticationMethod()) { + case "local": + $this->saveLocalPassword(); + break; + + default: + $type = $this->getAuthenticationMethod(); + call_user_func(array($type,'savePassword'),$this->getUsername(),$password); + } + } +} diff --git a/libraries/framework/classes/Template.php b/libraries/framework/classes/Template.php new file mode 100644 index 0000000..04d0a01 --- /dev/null +++ b/libraries/framework/classes/Template.php @@ -0,0 +1,105 @@ + + */ +class Template extends View +{ + private $filename; + public $outputFormat = 'html'; + public $blocks = array(); + + /** + * @param string $filename + * @param string $outputFormat + * @param array $vars + */ + public function __construct($filename='default',$outputFormat='html',array $vars=null) + { + $this->filename = $filename; + $this->outputFormat = preg_replace('/[^a-zA-Z]/','',$outputFormat); + + // Make sure the output format exists + if (!is_file(APPLICATION_HOME."/templates/{$this->outputFormat}/{$this->filename}.inc")) { + $this->filename = 'default'; + $this->outputFormat = 'html'; + } + + if (count($vars)) { + foreach ($vars as $name=>$value) { + $this->vars[$name] = $value; + } + } + } + + /** + * Returns all the rendered content of the template + * + * Template files must include a call to $this->includeBlocks(), + * when they're ready for content + * + * @return string + */ + public function render() + { + ob_start(); + include APPLICATION_HOME."/templates/{$this->outputFormat}/{$this->filename}.inc"; + return ob_get_clean(); + } + + /** + * Callback function for template files + * + * Renders blocks for the main content area, unless $panel is given. If $panel is given + * it will render any blocks that the controllers have assigned to that panel. + * + * Template files make calls to this function to render all the blocks that the controller + * has loaded for this Template. Controllers will populate the blocks array with content. + * If a template file can render content in a panel that is not the main content panel, + * the template file will need to include the panel's name in the includeBlocks() call. + * + * $this->blocks is a multi-dimensional array. The top level elements, non-array elements + * are for the default, main content area. Other panels will be arrays in $this->blocks with + * the panel name as the key. + * + * Panels are nothing but a name on a div, the $panel string can be whatever the template + * author thinks makes sense. Controllers are expected to know what the template authors + * have written. + * + * $this->blocks[] = "main content block one"; + * $this->blocks[] = "main content block two"; + * $this->blocks['panel-one'][] = "left sidebar block one"; + * $this->blocks['panel-one'][] = "left sidebar block two"; + * $this->blocks['panel-two'][] = "right sidebar block one"; + * + * @param string $panel + * @return string + */ + private function includeBlocks($panel=null) + { + ob_start(); + if ($panel) { + // Render any blocks for the given panel + if (isset($this->blocks[$panel]) && is_array($this->blocks[$panel])) { + foreach ($this->blocks[$panel] as $block) { + echo $block->render($this->outputFormat); + } + } + + } + else { + // Render only the blocks for the main content area + foreach ($this->blocks as $block) { + if (!is_array($block)) { + echo $block->render($this->outputFormat); + } + } + } + return ob_get_clean(); + } +} diff --git a/libraries/framework/classes/URL.php b/libraries/framework/classes/URL.php new file mode 100644 index 0000000..fab4d76 --- /dev/null +++ b/libraries/framework/classes/URL.php @@ -0,0 +1,157 @@ +parameters['somevar'] = $somevar; + * $url->somevar = $somevar; + * echo $url->getURL(); + * + * @copyright 2006-2009 City of Bloomington, Indiana. + * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt + * @author Cliff Ingham + */ +class URL +{ + private $scheme; + private $host; + private $path; + private $anchor; + + public $parameters = array(); + + public function __construct($script) + { + $script = urldecode($script); + + // If scheme wasn't provided add one to the start of the string + if (!preg_match('|://|',$script)) { + $scheme = $_SERVER['SERVER_PORT']==443 ? 'https://' : 'http://'; + $script = $scheme.$script; + } + + $url = parse_url($script); + $this->scheme = $url['scheme']; + $this->host = $url['host']; + $this->path = $url['path']; + if (isset($url['fragment'])) { + $this->anchor = $url['fragment']; + } + if (isset($url['query'])) { + parse_str($url['query'],$this->parameters); + } + } + + /** + * Returns just the base portion of the url + * @return string + */ + public function getScript() { + return $this->scheme.'://'.$this->host.$this->path; + } + + /** + * Returns the full, properly formatted and escaped URL + * @return string + */ + public function __toString() { + return $this->getURL(); + } + + /** + * Returns the full, properly formatted and escaped URL + * + * @return string + */ + public function getURL() + { + $url = $this->getScript(); + + if (count($this->parameters)) { + $url.= '?'.http_build_query($this->parameters,''); + } + + if ($this->anchor) { + $url.= '#'.$this->anchor; + } + return $url; + } + + /** + * Returns just the protocol (http://, https://) portion + * @return string + */ + public function getScheme() { + if (!$this->scheme) { + $this->scheme = 'http://'; + } + return $this->scheme; + } + + /** + * Sets the protocol for the URL (http, https) + * @param string $protocol + */ + public function setScheme($string) + { + if (!preg_match('|://|',$string)) { + $string .= '://'; + } + $this->scheme = $string; + } + + /** + * Cleans out any query parameters that had empty values + */ + public function purgeEmptyParameters() + { + $this->parameters = $this->array_filter_recursive($this->parameters); + } + + private function array_filter_recursive(array $input) + { + foreach ($input as &$value) { + if (is_array($value)) { + $value = $this->array_filter_recursive($value); + } + } + return array_filter($input); + } + + /** + * @param string $key + * @return string + */ + public function __get($key) + { + if (isset($this->parameters[$key])) { + return $this->parameters[$key]; + } + } + + /** + * @param string $key + * @param string $value + */ + public function __set($key,$value) + { + $this->parameters[$key] = $value; + } + + /** + * @param string $key + * @return boolean + */ + public function __isset($key) + { + return isset($this->parameters[$key]); + } + + /** + * @param string $key + */ + public function __unset($key) + { + unset($this->parameters[$key]); + } +} diff --git a/libraries/framework/classes/View.php b/libraries/framework/classes/View.php new file mode 100644 index 0000000..e122b95 --- /dev/null +++ b/libraries/framework/classes/View.php @@ -0,0 +1,89 @@ + + */ +abstract class View +{ + protected $vars = array(); + + abstract public function render(); + + /** + * Magic Method for setting object properties + * @param string $key + * @param mixed $value + */ + public function __set($key,$value) { + $this->vars[$key] = $value; + } + /** + * Magic method for getting object properties + * @param string $key + * @return mixed + */ + public function __get($key) + { + if (isset($this->vars[$key])) { + return $this->vars[$key]; + } + return null; + } + + /** + * @param string $key + * @return boolean + */ + public function __isset($key) { + return array_key_exists($key,$this->vars); + } + + /** + * Cleans strings for output + * + * There are more bad characters than htmlspecialchars deals with. We just want + * to add in some other characters to clean. While here, we might as well + * have it trim out the whitespace too. + * + * @param array|string $string + * @param CONSTANT $quotes Optional, the desired constant to use for the htmlspecidalchars call + * @return string + */ + public static function escape($input,$quotes=ENT_QUOTES) + { + if (is_array($input)) { + foreach ($input as $key=>$value) { + $input[$key] = self::escape($value,$quotes); + } + } + else { + $input = htmlspecialchars(trim($input),$quotes); + } + + return $input; + } + + /** + * Return the first $n words of the given string + * + * @param string $string Source string + * @param int $numWords Number of words + * @return string + */ + public static function limitWords($string,$numWords) + { + $output = ''; + $words = preg_split('/\s+/',$string); + $c = 0; + foreach ($words as $word) { + $output.= "$word "; + $c++; + if ($c >= $numWords) { + $output.= '...'; + break; + } + } + return $output; + } +} diff --git a/libraries/framework/classes/ZendDbResultIterator.php b/libraries/framework/classes/ZendDbResultIterator.php new file mode 100644 index 0000000..9bb8a2d --- /dev/null +++ b/libraries/framework/classes/ZendDbResultIterator.php @@ -0,0 +1,181 @@ + + * + */ +abstract class ZendDbResultIterator implements ArrayAccess,SeekableIterator,Countable +{ + protected $zend_db; + protected $select; + protected $result = array(); + + protected $paginator = null; + protected $itemsPerPage = null; + protected $currentPage = 1; + + private $valid = false; + private $cacheEnabled = true; + private $cache = array(); + private $key; + + + abstract public function find($fields=null,$order='',$limit=null,$groupBy=null); + abstract protected function loadResult($key); + + /** + * Creates an empty collection + * + * Setting itemsPerPage turns on pagination mode + * In pagination mode, this will only load the results for one page + */ + public function __construct($itemsPerPage=null,$currentPage=null) + { + $this->zend_db = Database::getConnection(); + $this->select = new Zend_Db_Select($this->zend_db); + + if ($itemsPerPage) { + $this->itemsPerPage = (integer)$itemsPerPage; + + if ($currentPage && $currentPage > 1) { + $this->currentPage = $currentPage; + } + } + } + + /** + * Runs the query and stores the results + * + * In pagination mode, this will only load the results for one page + */ + protected function populateList() + { + $this->result = array(); + if (!$this->itemsPerPage) { + $this->result = $this->zend_db->fetchAll($this->select); + } + else { + // Only load the results for one page + $this->paginator = new Zend_Paginator(new Zend_Paginator_Adapter_DbSelect($this->select)); + $this->paginator->setItemCountPerPage($this->itemsPerPage); + $this->paginator->setCurrentPageNumber($this->currentPage); + foreach ($this->paginator as $row) { + $this->result[] = $row; + } + } + } + + /** + * @return string + */ + public function getSQL() + { + return $this->select->__toString(); + } + + /** + * @return Zend_Paginator + */ + public function getPaginator() + { + return $this->paginator; + } + + // Array Access section + /** + * @param int $offset + * @return boolean + */ + public function offsetExists($offset) { + return array_key_exists($offset,$this->result); + } + /** + * Unimplemented stub requried for interface compliance + * @ignore + */ + public function offsetSet($offset,$value) { } // Read-only for now + /** + * Unimplemented stub requried for interface compliance + * @ignore + */ + public function offsetUnset($offset) { } // Read-only for now + /** + * @param int $offset + * @return mixed + */ + public function offsetGet($offset) + { + if ($this->offsetExists($offset)) { + return $this->loadResult($offset); + } + else { + throw new OutOfBoundsException('Invalid seek position'); + } + } + + + + // SPLIterator Section + /** + * Reset the pionter to the first element + */ + public function rewind() { + $this->key = 0; + } + /** + * Advance to the next element + */ + public function next() { + $this->key++; + } + /** + * Return the index of the current element + * @return int + */ + public function key() { + return $this->key; + } + /** + * @return boolean + */ + public function valid() { + return array_key_exists($this->key,$this->result); + } + /** + * @return mixed + */ + public function current() + { + return $this->loadResult($this->key); + } + /** + * @param int $index + */ + public function seek($index) + { + if (isset($this->result[$index])) { + $this->key = $index; + } + else { + throw new OutOfBoundsException('Invalid seek position'); + } + } + + /** + * @return Iterator + */ + public function getIterator() + { + return $this; + } + + // Countable Section + /** + * @return int + */ + public function count() + { + return count($this->result); + } +} diff --git a/libraries/framework/errorMessages.php b/libraries/framework/errorMessages.php new file mode 100644 index 0000000..c9ac474 --- /dev/null +++ b/libraries/framework/errorMessages.php @@ -0,0 +1,11 @@ + + */ +if (isset($_SESSION['errorMessages'])) { + $errorBlock = new Block('errorMessages.inc',array('errorMessages'=>$_SESSION['errorMessages'])); + echo $errorBlock->render($this->outputFormat); + unset($_SESSION['errorMessages']); +} diff --git a/libraries/framework/globalFunctions.php b/libraries/framework/globalFunctions.php new file mode 100644 index 0000000..f934dbb --- /dev/null +++ b/libraries/framework/globalFunctions.php @@ -0,0 +1,167 @@ + + * @package GlobalFunctions + */ +/** + * Load classes on the fly as needed + * @param string $class + */ +function autoload($class) +{ + if (file_exists(APPLICATION_HOME."/classes/$class.php")) { + require_once(APPLICATION_HOME."/classes/$class.php"); + } + elseif (file_exists(FRAMEWORK."/classes/$class.php")) { + require_once(FRAMEWORK."/classes/$class.php"); + } +} + +/** + * Provide nicely formatted error messages when PHP bombs out. + */ +function customErrorHandler ($errno, $errstr, $errfile, $errline) +{ + global $ERROR_REPORTING; + + if (isset($ERROR_REPORTING)) { + if (in_array('PRETTY_PRINT',$ERROR_REPORTING)) { + echo " +
+

from ".ADMINISTRATOR_NAME.": + There is an error in the code on this page that is through no fault of your own. + Errors of this sort need to be fixed immediately, though. + Please help us out by copying and pasting the following error message into an email and sending it to me at + ".ADMINISTRATOR_EMAIL.". +

+

Code Error: Error on line $errline of file $errfile:

+

$errstr

+
+ "; + } + if (in_array('EMAIL_ADMIN',$ERROR_REPORTING)) { + $subject = APPLICATION_NAME.' Error'; + $message = "\t$_SERVER[REQUEST_URI]\n\nError on line $errline of file $errfile:\n$errstr\n\n"; + $message.= print_r(debug_backtrace(),true); + mail(ADMINISTRATOR_EMAIL,$subject,$message,"From: apache@$_SERVER[SERVER_NAME]"); + } + + if (in_array('EMAIL_USER',$ERROR_REPORTING) + && isset($_SESSION['USER']) + && $_SESSION['USER']->getEmail()) { + $subject = APPLICATION_NAME.' Error'; + $message = "\t$_SERVER[REQUEST_URI]\n\nError on line $errline of file $errfile:\n$errstr\n\n"; + $message.= print_r(debug_backtrace(),true); + mail($_SESSION['USER']->getEmail(), + $subject, + $message, + "From: apache@$_SERVER[SERVER_NAME]"); + } + if (in_array('SKIDDER',$ERROR_REPORTING)) { + $message = "Error on line $errline of file $errfile:\n$errstr\n"; + $message.= print_r(debug_backtrace(),true); + + $skidder = curl_init(SKIDDER_URL); + curl_setopt($skidder,CURLOPT_POST,true); + curl_setopt($skidder,CURLOPT_HEADER,true); + curl_setopt($skidder,CURLOPT_RETURNTRANSFER,true); + curl_setopt($skidder, + CURLOPT_POSTFIELDS, + array('application_id'=>SKIDDER_APPLICATION_ID, + 'script'=>$_SERVER['REQUEST_URI'], + 'type'=>$errstr, + 'message'=>$message)); + curl_exec($skidder); + } + } +} +if (ERROR_REPORTING != 'PHP_DEFAULT') { + set_error_handler('customErrorHandler'); +} + +/** + * Object oriented exceptions are handled differently from other PHP errors. + */ +function customExceptionHandler($exception) +{ + global $ERROR_REPORTING; + + if (isset($ERROR_REPORTING)) { + if (in_array('PRETTY_PRINT',$ERROR_REPORTING)) { + echo " +
+

from ".ADMINISTRATOR_NAME.": + There is an error in the code on this page that is through no fault of your own. + Errors of this sort need to be fixed immediately, though. + Please help me out by copying and pasting the following error message into an email and sending it to me at + ".ADMINISTRATOR_EMAIL.". +

+

Uncaught exception: + Exception on line {$exception->getLine()} of file {$exception->getFile()}: +

+

{$exception->getMessage()}

+
+ "; + } + if (in_array('EMAIL_ADMIN',$ERROR_REPORTING)) { + $subject = APPLICATION_NAME.' Exception'; + $message = "\t$_SERVER[REQUEST_URI]\n\nException on line {$exception->getLine()} of file {$exception->getFile()}:\n{$exception->getMessage()}\n\n"; + $message.= print_r(debug_backtrace(),true); + mail(ADMINISTRATOR_EMAIL,$subject,$message,"From: apache@$_SERVER[SERVER_NAME]"); + } + if (in_array('EMAIL_USER',$ERROR_REPORTING) + && isset($_SESSION['USER']) + && $_SESSION['USER']->getEmail()) { + $subject = APPLICATION_NAME.' Exception'; + $message = "\t$_SERVER[REQUEST_URI]\n\nException on line {$exception->getLine()} of file {$exception->getFile()}:\n{$exception->getMessage()}\n\n"; + $message.= print_r(debug_backtrace(),true); + mail($_SESSION['USER']->getEmail(), + $subject, + $message, + "From: apache@$_SERVER[SERVER_NAME]"); + } + if (in_array('SKIDDER',$ERROR_REPORTING)) { + $message = "Error on line {$exception->getLine()} of file {$exception->getFile()}:\n{$exception->getMessage()}\n"; + $message.= print_r(debug_backtrace(),true); + + $skidder = curl_init(SKIDDER_URL); + curl_setopt($skidder,CURLOPT_POST,true); + curl_setopt($skidder,CURLOPT_HEADER,true); + curl_setopt($skidder,CURLOPT_RETURNTRANSFER,true); + curl_setopt($skidder, + CURLOPT_POSTFIELDS, + array('application_id'=>SKIDDER_APPLICATION_ID, + 'script'=>$_SERVER['REQUEST_URI'], + 'type'=>'Uncaught Exception', + 'message'=>$message)); + curl_exec($skidder); + } + } +} +if (ERROR_REPORTING != 'PHP_DEFAULT') { + set_exception_handler('customExceptionHandler'); +} + +/** + * Checks if the user is logged in and is supposed to have acces to the resource + * + * This is implemented by checking against a Zend_Acl object + * The Zend_Acl should be created in configuration.inc + * @param Zend_Acl_Resource|string $resource + * @return boolean + */ +function userIsAllowed($resource) +{ + global $ZEND_ACL; + if (isset($_SESSION['USER'])) { + foreach ($_SESSION['USER']->getRoles() as $role) { + if ($ZEND_ACL->isAllowed($role,$resource)) { + return true; + } + } + } + return false; +} \ No newline at end of file diff --git a/libraries/framework/inflections.inc b/libraries/framework/inflections.inc new file mode 100644 index 0000000..00739dc --- /dev/null +++ b/libraries/framework/inflections.inc @@ -0,0 +1,68 @@ + + * Copyright (c) 2006, Cake Software Foundation, Inc. + * 1785 E. Sahara Avenue, Suite 490-204 + * Las Vegas, Nevada 89104 + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice. + * @copyright Copyright (c) 2006, Cake Software Foundation, Inc. + * @license http://www.opensource.org/licenses/mit-license.php The MIT License + * + * Modified to work in City of Bloomington's Framework + * Relicensed under GPL + * Redistributions must retain all copyright notices + * @copyright 2006 City of Bloomington, Indiana + * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt + */ +/** + * This is a key => value array of regex used to match words. + * If key matches then the value is returned. + * + * $pluralRules = array('/(s)tatus$/i' => '\1\2tatuses', '/^(ox)$/i' => '\1\2en', '/([m|l])ouse$/i' => '\1ice'); + */ + $pluralRules = array(); +/** + * This is a key only array of plural words that should not be inflected. + * Notice the last comma + * + * $uninflectedPlural = array('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox'); + */ + $uninflectedPlural = array(); +/** + * This is a key => value array of plural irregular words. + * If key matches then the value is returned. + * + * $irregularPlural = array('atlas' => 'atlases', 'beef' => 'beefs', 'brother' => 'brothers') + */ + $irregularPlural = array(); +/** + * This is a key => value array of regex used to match words. + * If key matches then the value is returned. + * + * $singularRules = array('/(s)tatuses$/i' => '\1\2tatus', '/(matr)ices$/i' =>'\1ix','/(vert|ind)ices$/i') + */ + $singularRules = array(); +/** + * This is a key only array of singular words that should not be inflected. + * You should not have to change this value below if you do change it use same format + * as the $uninflectedPlural above. + */ + $uninflectedSingular = $uninflectedPlural; +/** + * This is a key => value array of singular irregular words. + * Most of the time this will be a reverse of the above $irregularPlural array + * You should not have to change this value below if you do change it use same format + * + * $irregularSingular = array('atlases' => 'atlas', 'beefs' => 'beef', 'brothers' => 'brother') + */ + $irregularSingular = array_flip($irregularPlural); +?> \ No newline at end of file diff --git a/scripts/generateBlocks.php b/scripts/generateBlocks.php new file mode 100644 index 0000000..afef13b --- /dev/null +++ b/scripts/generateBlocks.php @@ -0,0 +1,330 @@ + + */ +include '../configuration.inc'; +$zend_db = Database::getConnection(); + +foreach ($zend_db->listTables() as $tableName) { + $fields = array(); + $primary_keys = array(); + foreach ($zend_db->describeTable($tableName) as $row) { + $type = preg_replace("/[^a-z]/","",strtolower($row['DATA_TYPE'])); + + // Translate database datatypes into PHP datatypes + if (preg_match('/int/',$type)) { + $type = 'int'; + } + if (preg_match('/enum/',$type) || preg_match('/varchar/',$type)) { + $type = 'string'; + } + + $fields[] = array('field'=>$row['COLUMN_NAME'],'type'=>$type); + + if ($row['PRIMARY']) { + $primary_keys[] = $row['COLUMN_NAME']; + } + } + + // Only generate code for tables that have a single-column primary key + // Code for other tables will need to be created by hand + if (count($primary_keys) != 1) { + continue; + } + $key = $primary_keys[0]; + + $tableName = strtolower($tableName); + $className = Inflector::classify($tableName); + $variableName = Inflector::singularize($tableName); + $acl_resource = ucfirst($tableName); + + /** + * Generate the list block + */ + $getId = "get".ucwords($key); + $HTML = "
+

+ + Add + + \"; + } + ?> + {$className}s +

+
    {$variableName}List as \${$variableName}) { + \$editButton = ''; + if (userIsAllowed('$acl_resource')) { + \$url = new URL(BASE_URL.'/$tableName/update$className.php'); + \$url->$key = \${$variableName}->{$getId}(); + \$editButton = \" + + \"; + } + echo \"
  • \$editButton \$$variableName
  • \"; + } + ?> +
+
"; + +$contents = " +$HTML"; + + $dir = APPLICATION_HOME."/scripts/stubs/blocks/$tableName"; + if (!is_dir($dir)) { + mkdir($dir,0770,true); + } + file_put_contents("$dir/{$variableName}List.inc",$contents); + + +/** + * Generate the addForm + */ +$HTML = "

Add $className

+
\"> +
$className Info + +"; + foreach ($fields as $field) { + if ($field['field'] != $key) { + $fieldFunctionName = ucwords($field['field']); + switch ($field['type']) { + case 'date': + $HTML.=" + + + "; + break; + + case 'datetime': + case 'timestamp': + $HTML.=" + + + "; + break; + + case 'text': + $HTML.= " + + + + "; + break; + + default: + $HTML.= " + + + + "; + } + } + } + $HTML.= " +
+ + \" /> +
+ + \" /> + + +
+
+
+ + + +
+
"; + +$contents = " +$HTML"; +file_put_contents("$dir/add{$className}Form.inc",$contents); + +/** + * Generate the Update Form + */ +$HTML = "

Update $className

+
\"> +
$className Info + {$variableName}->{$getId}(); ?>\" /> + +"; + foreach ($fields as $field) { + if ($field['field'] != $key) { + $fieldFunctionName = ucwords($field['field']); + switch ($field['type']) { + case 'date': + $HTML.=" + + + "; + break; + + case 'datetime': + case 'timestamp': + $HTML.=" + + + "; + break; + + case 'text': + $HTML.= " + + + + "; + break; + + default: + $HTML.= " + + + + "; + } + } + } + $HTML.= " +
+ + \" /> +
+ + \" /> + + +
+
{$variableName}->get$fieldFunctionName(); ?>\" /> +
+ + + +
+
"; +$contents = " +$HTML"; +file_put_contents("$dir/update{$className}Form.inc",$contents); + +echo "$className\n"; +} diff --git a/scripts/generateClasses.php b/scripts/generateClasses.php new file mode 100644 index 0000000..b59b740 --- /dev/null +++ b/scripts/generateClasses.php @@ -0,0 +1,382 @@ + + */ +include '../configuration.inc'; +$zend_db = Database::getConnection(); + +foreach ($zend_db->listTables() as $tableName) { + $fields = array(); + $primary_keys = array(); + foreach ($zend_db->describeTable($tableName) as $row) { + $type = preg_replace("/[^a-z]/","",strtolower($row['DATA_TYPE'])); + + // Translate database datatypes into PHP datatypes + if (preg_match('/int/',$type)) { + $type = 'int'; + } + if (preg_match('/enum/',$type) || preg_match('/varchar/',$type)) { + $type = 'string'; + } + + $fields[] = array('field'=>$row['COLUMN_NAME'],'type'=>$type); + + if ($row['PRIMARY']) { + $primary_keys[] = $row['COLUMN_NAME']; + } + } + + // Only generate code for tables that have a single-column primary key + // Code for other tables will need to be created by hand + if (count($primary_keys) != 1) { + continue; + } + $key = $primary_keys[0]; + + $tableName = strtolower($tableName); + $className = Inflector::classify($tableName); + //-------------------------------------------------------------------------- + // Constructor + //-------------------------------------------------------------------------- + $constructor = " + /** + * Populates the object with data + * + * Passing in an associative array of data will populate this object without + * hitting the database. + * + * Passing in a scalar will load the data from the database. + * This will load all fields in the table as properties of this class. + * You may want to replace this with, or add your own extra, custom loading + * + * @param int|array \$$key + */ + public function __construct(\$$key=null) + { + if (\$$key) { + if (is_array(\$$key)) { + \$result = \$$key; + } + else { + \$zend_db = Database::getConnection(); + \$sql = 'select * from $tableName where $key=?'; + \$result = \$zend_db->fetchRow(\$sql,array(\$$key)); + } + + if (\$result) { + foreach (\$result as \$field=>\$value) { + if (\$value) { + \$this->\$field = \$value; + } + } + } + else { + throw new Exception('$tableName/unknown$className'); + } + } + else { + // This is where the code goes to generate a new, empty instance. + // Set any default values for properties that need it here + } + } + "; + + //-------------------------------------------------------------------------- + // Properties + //-------------------------------------------------------------------------- + $properties = ''; + $linkedProperties = array(); + foreach ($fields as $field) { + $properties.= "\tprivate \$$field[field];\n"; + + if (substr($field['field'],-3) == '_id') { + $linkedProperties[] = $field['field']; + } + } + + if (count($linkedProperties)) { + $properties.="\n\n"; + foreach ($linkedProperties as $property) { + $field = substr($property,0,-3); + $properties.= "\tprivate \$$field;\n"; + } + } + + //-------------------------------------------------------------------------- + // Getters + //-------------------------------------------------------------------------- + $getters = ''; + foreach ($fields as $field) { + $fieldFunctionName = ucwords($field['field']); + + switch ($field['type']) + { + case 'date': + case 'datetime': + case 'timestamp': + $getters.= " + /** + * Returns the date/time in the desired format + * + * Format is specified using PHP's date() syntax + * http://www.php.net/manual/en/function.date.php + * If no format is given, the Date object is returned + * + * @param string \$format + * @return string|DateTime + */ + public function get$fieldFunctionName(\$format=null) + { + if (\$format && \$this->$field[field]) { + return \$this->$field[field]->format(\$format); + } + else { + return \$this->$field[field]; + } + } +"; + break; + + default: $getters.= " + /** + * @return $field[type] + */ + public function get$fieldFunctionName() + { + return \$this->$field[field]; + } +"; + } + } + + foreach ($linkedProperties as $property) { + $field = substr($property,0,-3); + $fieldFunctionName = ucwords($field); + $getters.= " + /** + * @return $fieldFunctionName + */ + public function get$fieldFunctionName() + { + if (\$this->$property) { + if (!\$this->$field) { + \$this->$field = new $fieldFunctionName(\$this->$property); + } + return \$this->$field; + } + return null; + } +"; + } + + + //-------------------------------------------------------------------------- + // Setters + //-------------------------------------------------------------------------- + $setters = ''; + foreach ($fields as $field) { + if ($field['field'] != $key) { + $fieldFunctionName = ucwords($field['field']); + switch ($field['type']) { + case 'int': + if (in_array($field['field'],$linkedProperties)) { + $property = substr($field['field'],0,-3); + $object = ucfirst($property); + $setters.= " + /** + * @param $field[type] \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + \$this->$property = new $object(\$int); + \$this->$field[field] = \$$field[type]; + } +"; + } + else { + $setters.= " + /** + * @param $field[type] \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + \$this->$field[field] = preg_replace(\"/[^0-9]/\",\"\",\$$field[type]); + } +"; + } + break; + + case 'string': + $setters.= " + /** + * @param $field[type] \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + \$this->$field[field] = trim(\$$field[type]); + } +"; + break; + + case 'date': + case 'datetime': + case 'timestamp': + $setters.= " + /** + * Sets the date + * + * Date arrays should match arrays produced by getdate() + * + * Date string formats should be in something strtotime() understands + * http://www.php.net/manual/en/function.strtotime.php + * + * @param int|string|array \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + if (\$$field[type]) { + \$this->$field[field] = new Date(\$$field[type]); + } + else { + \$this->$field[field] = null; + } + } +"; + break; + + case 'float': + $setters.= " + /** + * @param $field[type] \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + \$this->$field[field] = preg_replace(\"/[^0-9.\-]/\",\"\",\$$field[type]); + } +"; + break; + + case 'bool': + $setters.= " + /** + * @param boolean \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + \$this->$field[field] = \$$field[type] ? true : false; + } +"; + break; + + default: + $setters.= " + /** + * @param $field[type] \$$field[type] + */ + public function set$fieldFunctionName(\$$field[type]) + { + \$this->$field[field] = \$$field[type]; + } +"; + } + } + } + + foreach ($linkedProperties as $field) { + $property = substr($field,0,-3); + $object = ucfirst($property); + $setters.= " + /** + * @param $object \$$property + */ + public function set$object(\$$property) + { + \$this->$field = \${$property}->getId(); + \$this->$property = \$$property; + } +"; + } + + //-------------------------------------------------------------------------- + // Output the class + //-------------------------------------------------------------------------- +$contents = "validate(); + + \$data = array(); +"; + foreach ($fields as $field) { + if ($field['field'] != $key) { + $contents.="\t\t\$data['$field[field]'] = \$this->$field[field] ? \$this->$field[field] : null;\n"; + } + } +$contents.= " + if (\$this->$key) { + \$this->update(\$data); + } + else { + \$this->insert(\$data); + } + } + + private function update(\$data) + { + \$zend_db = Database::getConnection(); + \$zend_db->update('$tableName',\$data,\"$key='{\$this->$key}'\"); + } + + private function insert(\$data) + { + \$zend_db = Database::getConnection(); + \$zend_db->insert('$tableName',\$data); + \$this->$key = \$zend_db->lastInsertId('$tableName','$key'); + } + + //---------------------------------------------------------------- + // Generic Getters + //---------------------------------------------------------------- +$getters + //---------------------------------------------------------------- + // Generic Setters + //---------------------------------------------------------------- +$setters + + //---------------------------------------------------------------- + // Custom Functions + // We recommend adding all your custom code down here at the bottom + //---------------------------------------------------------------- +} +"; + $dir = APPLICATION_HOME.'/scripts/stubs/classes'; + if (!is_dir($dir)) { + mkdir($dir,0770,true); + } + file_put_contents("$dir/$className.php",$contents); + echo "$className\n"; +} diff --git a/scripts/generateControllers.php b/scripts/generateControllers.php new file mode 100644 index 0000000..522a041 --- /dev/null +++ b/scripts/generateControllers.php @@ -0,0 +1,135 @@ + + */ +include '../configuration.inc'; +$zend_db = Database::getConnection(); + +foreach ($zend_db->listTables() as $tableName) { + $fields = array(); + $primary_keys = array(); + foreach ($zend_db->describeTable($tableName) as $row) { + $type = preg_replace("/[^a-z]/","",strtolower($row['DATA_TYPE'])); + + // Translate database datatypes into PHP datatypes + if (preg_match('/int/',$type)) { + $type = 'int'; + } + if (preg_match('/enum/',$type) || preg_match('/varchar/',$type)) { + $type = 'string'; + } + + $fields[] = array('field'=>$row['COLUMN_NAME'],'type'=>$type); + + if ($row['PRIMARY']) { + $primary_keys[] = $row['COLUMN_NAME']; + } + } + + // Only generate code for tables that have a single-column primary key + // Code for other tables will need to be created by hand + if (count($primary_keys) != 1) { + continue; + } + $key = $primary_keys[0]; + + $tableName = strtolower($tableName); + $className = Inflector::classify($tableName); + $variableName = Inflector::singularize($tableName); + $acl_resource = ucfirst($tableName); + +/** + * Generate home.php + */ +$PHP = " +\${$variableName}List = new {$className}List(); +\${$variableName}List->find(); + +\$template = new Template(); +\$template->blocks[] = new Block('{$variableName}s/{$variableName}List.inc',array('{$variableName}List'=>\${$variableName}List)); +echo \$template->render();"; + +$contents = "\$value) { + \$set = 'set'.ucfirst(\$field); + \${$variableName}->\$set(\$value); + } + + try { + \${$variableName}->save(); + header('Location: '.BASE_URL.'/$tableName'); + exit(); + } + catch(Exception \$e) { + \$_SESSION['errorMessages'][] = \$e; + } +} + +\$template = new Template(); +\$template->blocks[] = new Block('{$variableName}s/add{$className}Form.inc'); +echo \$template->render();"; +$contents = "\$value) { + \$set = 'set'.ucfirst(\$field); + \${$variableName}->\$set(\$value); + } + + try { + \${$variableName}->save(); + header('Location: '.BASE_URL.'/$tableName'); + exit(); + } + catch (Exception \$e) { + \$_SESSION['errorMessages'][] = \$e; + } +} + +\$template = new Template(); +\$template->blocks[] = new Block('{$variableName}s/update{$className}Form.inc',array('{$variableName}'=>\${$variableName})); +echo \$template->render();"; +$contents = " + */ +include '../configuration.inc'; +$zend_db = Database::getConnection(); + +foreach ($zend_db->listTables() as $tableName) { + $fields = array(); + $primary_keys = array(); + foreach ($zend_db->describeTable($tableName) as $row) { + $type = preg_replace("/[^a-z]/","",strtolower($row['DATA_TYPE'])); + + // Translate database datatypes into PHP datatypes + if (preg_match('/int/',$type)) { + $type = 'int'; + } + if (preg_match('/enum/',$type) || preg_match('/varchar/',$type)) { + $type = 'string'; + } + + $fields[] = array('field'=>$row['COLUMN_NAME'],'type'=>$type); + + if ($row['PRIMARY']) { + $primary_keys[] = $row['COLUMN_NAME']; + } + } + + // Only generate code for tables that have a single-column primary key + // Code for other tables will need to be created by hand + if (count($primary_keys) != 1) { + continue; + } + $key = $primary_keys[0]; + + $tableName = strtolower($tableName); + $className = Inflector::classify($tableName); + + //-------------------------------------------------------------------------- + // Output the class + //-------------------------------------------------------------------------- +$contents = "find(\$fields); + } + } + + /** + * Populates the collection + * + * @param array \$fields + * @param string|array \$order Multi-column sort should be given as an array + * @param int \$limit + * @param string|array \$groupBy Multi-column group by should be given as an array + */ + public function find(\$fields=null,\$order='$key',\$limit=null,\$groupBy=null) + { + \$this->select->from('$tableName'); + + // Finding on fields from the $tableName table is handled here + if (count(\$fields)) { + foreach (\$fields as \$key=>\$value) { + \$this->select->where(\"\$key=?\",\$value); + } + } + + // Finding on fields from other tables requires joining those tables. + // You can handle fields from other tables by adding the joins here + // If you add more joins you probably want to make sure that the + // above foreach only handles fields from the $tableName table. + + \$this->select->order(\$order); + if (\$limit) { + \$this->select->limit(\$limit); + } + if (\$groupBy) { + \$this->select->group(\$groupBy); + } + \$this->populateList(); + } + + /** + * Hydrates all the $className objects from a database result set + * + * This is a callback function, called from ZendDbResultIterator. It is + * called once per row of the result. + * + * @param int \$key The index of the result row to load + * @return $className + */ + protected function loadResult(\$key) + { + return new $className(\$this->result[\$key]); + } +} +"; + $dir = APPLICATION_HOME.'/scripts/stubs/classes'; + if (!is_dir($dir)) { + mkdir($dir,0770,true); + } + file_put_contents("$dir/{$className}List.php",$contents); + echo "$className\n"; +} diff --git a/scripts/generateTests.php b/scripts/generateTests.php new file mode 100644 index 0000000..392b739 --- /dev/null +++ b/scripts/generateTests.php @@ -0,0 +1,250 @@ + + */ +include '../configuration.inc'; +$dir = APPLICATION_HOME.'/scripts/stubs/tests'; +if (!is_dir($dir)) { + mkdir($dir,0770,true); +} + +$dir = APPLICATION_HOME.'/scripts/stubs/tests/DatabaseTests'; +if (!is_dir($dir)) { + mkdir($dir,0770,true); +} + +$dir = APPLICATION_HOME.'/scripts/stubs/tests/UnitTests'; +if (!is_dir($dir)) { + mkdir($dir,0770,true); +} + +$dir = APPLICATION_HOME.'/scripts/stubs/tests'; + + +$zend_db = Database::getConnection(); +$classes = array(); +foreach ($zend_db->listTables() as $tableName) { + $fields = array(); + $primary_keys = array(); + foreach ($zend_db->describeTable($tableName) as $row) { + $type = preg_replace("/[^a-z]/","",strtolower($row['DATA_TYPE'])); + + // Translate database datatypes into PHP datatypes + if (preg_match('/int/',$type)) { + $type = 'int'; + } + if (preg_match('/enum/',$type) || preg_match('/varchar/',$type)) { + $type = 'string'; + } + + $fields[] = array('field'=>$row['COLUMN_NAME'],'type'=>$type); + + if ($row['PRIMARY']) { + $primary_keys[] = $row['COLUMN_NAME']; + } + } + + // Only generate code for tables that have a single-column primary key + // Code for other tables will need to be created by hand + if (count($primary_keys) != 1) { + continue; + } + $key = $primary_keys[0]; + + $tableName = strtolower($tableName); + $className = Inflector::classify($tableName); + $classes[] = $className; + + $variable = strtolower($className); + +//------------------------------------------------------------------------------ +// Generate the Unit Tests +//------------------------------------------------------------------------------ +$contents = "validate(); + \$this->fail('Missing name failed to throw exception'); + } + catch (Exception \$e) { + + } + + \${$variable}->setName('Test {$className}'); + \${$variable}->validate(); + } +} +"; +file_put_contents("$dir/UnitTests/{$className}UnitTest.php",$contents); + +//------------------------------------------------------------------------------ +// Generate the Database Tests +//------------------------------------------------------------------------------ +$contents = "setName('Test {$className}'); + try { + \${$variable}->save(); + \$id = \${$variable}->getId(); + \$this->assertGreaterThan(0,\$id); + } + catch (Exception \$e) { + \$this->fail(\$e->getMessage()); + } + + \${$variable} = new {$className}(\$id); + \$this->assertEquals(\${$variable}->getName(),'Test {$className}'); + + \${$variable}->setName('Test'); + \${$variable}->save(); + + \${$variable} = new {$className}(\$id); + \$this->assertEquals(\${$variable}->getName(),'Test'); + } +} +"; +file_put_contents("$dir/DatabaseTests/{$className}DbTest.php",$contents); + +//------------------------------------------------------------------------------ +// Generate the Database List Tests +//------------------------------------------------------------------------------ +$contents = "query('select id from $tableName order by id'); + \$result = \$query->fetchAll(); + + \$list = new {$className}List(); + \$list->find(); + \$this->assertEquals(\$list->getSort(),'id'); + + foreach (\$list as \$i=>\${$variable}) { + \$this->assertEquals(\${$variable}->getId(),\$result[\$i]['id']); + } + } +} +"; +file_put_contents("$dir/DatabaseTests/{$className}ListDbTest.php",$contents); + +echo "$className\n"; +} + +//------------------------------------------------------------------------------ +// Generate the All Tests Suite +//------------------------------------------------------------------------------ +$contents = "addTest(UnitTests::suite()); + \$suite->addTest(DatabaseTests::suite()); + return \$suite; + } +} +"; +file_put_contents("$dir/AllTests.php",$contents); + +//------------------------------------------------------------------------------ +// Generate the All Tests Suite +//------------------------------------------------------------------------------ +$contents = "addTestSuite('{$className}DbTest');\n"; + $contents.= "\t\t\$suite->addTestSuite('{$className}ListDbTest');\n"; +} +$contents.= " + return \$suite; + } +} +"; +file_put_contents("$dir/DatabaseTests.php",$contents); + +//------------------------------------------------------------------------------ +// Generate the Unit Tests Suite +//------------------------------------------------------------------------------ +$contents = "addTestSuite('{$className}UnitTest');\n"; +} +$contents.= " + return \$suite; + } +} +"; +file_put_contents("$dir/UnitTests.php",$contents); diff --git a/scripts/mysql.sql b/scripts/mysql.sql new file mode 100644 index 0000000..d078a60 --- /dev/null +++ b/scripts/mysql.sql @@ -0,0 +1,75 @@ +-- @copyright 2009 City of Bloomington, Indiana +-- @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt +-- @author Cliff Ingham +create table people ( + id int unsigned not null primary key auto_increment, + firstname varchar(128) not null, + lastname varchar(128) not null, + email varchar(255) not null +) engine=InnoDB; + +create table users ( + id int unsigned not null primary key auto_increment, + person_id int unsigned not null unique, + username varchar(30) not null unique, + password varchar(32), + authenticationMethod varchar(40) not null default 'LDAP', + foreign key (person_id) references people(id) +) engine=InnoDB; + +create table roles ( + id int unsigned not null primary key auto_increment, + name varchar(30) not null unique +) engine=InnoDB; +insert roles values(1,'Administrator'); +insert roles values(2,'Editor'); + +create table user_roles ( + user_id int unsigned not null, + role_id int unsigned not null, + primary key (user_id,role_id), + foreign key(user_id) references users (id), + foreign key(role_id) references roles (id) +) engine=InnoDB; + +create table cemeteries ( + id int unsigned not null primary key auto_increment, + name varchar(128) not null +) engine=InnoDB; + +create table deeds ( + id int unsigned not null primary key auto_increment, + section varchar(5), + lot varchar(5), + lastname1 varchar(20), + firstname1 varchar(20), + middleInitial1 char(1), + lastname2 varchar(20), + firstname2 varchar(20), + middleInitial2 char(1), + issueDate date, + notes text, + lot2 char(5), + cemetery_id int unsigned, + foreign key (cemetery_id) references cemeteries(id) +) engine=InnoDB; + +create table internments ( + id int(11) unsigned not null primary key auto_increment, + section varchar(5), + lot varchar(5), + book varchar(4), + pageNumber varchar(5), + deceasedDate date, + lastname varchar(20), + firstname varchar(20), + middleInitial char(1), + birthPlace varchar(20), + lastResidence varchar(20), + age int unsigned, + sex enum('M','F'), + cemetery_id int unsigned, + notes text, + lot2 varchar(5), + foreign key (cemetery_id) references cemeteries(id) +) engine=InnoDB; diff --git a/scripts/scaffold b/scripts/scaffold new file mode 100755 index 0000000..99aa99d --- /dev/null +++ b/scripts/scaffold @@ -0,0 +1,7 @@ +#!/bin/bash +PHP=/usr/local/bin/php +$PHP generateBlocks.php +$PHP generateControllers.php +$PHP generateClasses.php +$PHP generateLists.php +$PHP generateTests.php diff --git a/templates/html/default.inc b/templates/html/default.inc new file mode 120000 index 0000000..3931dc4 --- /dev/null +++ b/templates/html/default.inc @@ -0,0 +1 @@ +two-column.inc \ No newline at end of file diff --git a/templates/html/full-width.inc b/templates/html/full-width.inc new file mode 100644 index 0000000..174bc2f --- /dev/null +++ b/templates/html/full-width.inc @@ -0,0 +1,29 @@ + + + +layout = 'full-width'; + $dir = dirname(__FILE__); + include $dir.'/partials/header.inc'; +?> + + + +
+
+ includeBlocks(); + ?> +
+
+ + + + diff --git a/templates/html/partials/banner.inc b/templates/html/partials/banner.inc new file mode 100644 index 0000000..e94ff3b --- /dev/null +++ b/templates/html/partials/banner.inc @@ -0,0 +1,6 @@ + diff --git a/templates/html/partials/footer.inc b/templates/html/partials/footer.inc new file mode 100644 index 0000000..4ef721a --- /dev/null +++ b/templates/html/partials/footer.inc @@ -0,0 +1,10 @@ + diff --git a/templates/html/partials/garbageCollection.inc b/templates/html/partials/garbageCollection.inc new file mode 100644 index 0000000..363c3c3 --- /dev/null +++ b/templates/html/partials/garbageCollection.inc @@ -0,0 +1,8 @@ + + + + + + + + + New Application + diff --git a/templates/html/partials/menubar.inc b/templates/html/partials/menubar.inc new file mode 100644 index 0000000..616169a --- /dev/null +++ b/templates/html/partials/menubar.inc @@ -0,0 +1,12 @@ + diff --git a/templates/html/partials/panel-one.inc b/templates/html/partials/panel-one.inc new file mode 100644 index 0000000..fcaf65b --- /dev/null +++ b/templates/html/partials/panel-one.inc @@ -0,0 +1,7 @@ +
+ +
diff --git a/templates/html/partials/panel-two.inc b/templates/html/partials/panel-two.inc new file mode 100644 index 0000000..64c0d9f --- /dev/null +++ b/templates/html/partials/panel-two.inc @@ -0,0 +1,2 @@ +
+
diff --git a/templates/html/partials/panel-widgets/Users.inc b/templates/html/partials/panel-widgets/Users.inc new file mode 100644 index 0000000..b5825f3 --- /dev/null +++ b/templates/html/partials/panel-widgets/Users.inc @@ -0,0 +1,7 @@ +
+

Admin

+ +
diff --git a/templates/html/three-column.inc b/templates/html/three-column.inc new file mode 100644 index 0000000..03873fc --- /dev/null +++ b/templates/html/three-column.inc @@ -0,0 +1,33 @@ + + + +layout = 'three-column'; + $dir = dirname(__FILE__); + include $dir.'/partials/header.inc'; +?> + + + +
+ +
+ includeBlocks(); + ?> +
+
+ + + + diff --git a/templates/html/two-column.inc b/templates/html/two-column.inc new file mode 100644 index 0000000..d3ff663 --- /dev/null +++ b/templates/html/two-column.inc @@ -0,0 +1,32 @@ + + + +layout = 'two-column'; + $dir = dirname(__FILE__); + include $dir.'/partials/header.inc'; +?> + + + +
+ +
+ includeBlocks(); + ?> +
+
+ + + + diff --git a/tests/AllTests.php b/tests/AllTests.php new file mode 100644 index 0000000..62fa33f --- /dev/null +++ b/tests/AllTests.php @@ -0,0 +1,16 @@ +addTest(UnitTests::suite()); + $suite->addTest(DatabaseTests::suite()); + return $suite; + } +} diff --git a/tests/CodeFormattingTests.sh b/tests/CodeFormattingTests.sh new file mode 100755 index 0000000..4985274 --- /dev/null +++ b/tests/CodeFormattingTests.sh @@ -0,0 +1,11 @@ +#!/bin/bash +PHPCS=/usr/local/PHP_CodeSniffer/scripts/phpcs +STANDARD=COB + +for FILE in `ls ../classes/`; + do $PHPCS --standard=$STANDARD ../classes/$FILE; +done; + +for FILE in `find ../html/ -name '*.php'`; + do $PHPCS --standard=$STANDARD $FILE; +done diff --git a/tests/DatabaseTests.php b/tests/DatabaseTests.php new file mode 100644 index 0000000..4467c5f --- /dev/null +++ b/tests/DatabaseTests.php @@ -0,0 +1,34 @@ +addTestSuite('RoleListDbTest'); + $suite->addTestSuite('RoleDbTest'); + $suite->addTestSuite('UserListDbTest'); + $suite->addTestSuite('UserDbTest'); + + return $suite; + } +} diff --git a/tests/DatabaseTests/RoleDbTest.php b/tests/DatabaseTests/RoleDbTest.php new file mode 100644 index 0000000..51590ea --- /dev/null +++ b/tests/DatabaseTests/RoleDbTest.php @@ -0,0 +1,34 @@ +setName('Test Role'); + try { + $role->save(); + $id = $role->getId(); + $this->assertGreaterThan(0,$id); + } + catch (Exception $e) { + $this->fail($e->getMessage()); + } + + $role = new Role($id); + $this->assertEquals($role->getName(),'Test Role'); + + $role->setName('Test'); + $role->save(); + + $role = new Role($id); + $this->assertEquals($role->getName(),'Test'); + } +} diff --git a/tests/DatabaseTests/RoleListDbTest.php b/tests/DatabaseTests/RoleListDbTest.php new file mode 100644 index 0000000..7ad8d78 --- /dev/null +++ b/tests/DatabaseTests/RoleListDbTest.php @@ -0,0 +1,30 @@ +query('select id from roles order by name'); + $result = $query->fetchAll(); + + $list = new RoleList(); + $list->find(); + $this->assertEquals($list->getSort(),'name'); + + foreach ($list as $i=>$role) + { + $this->assertEquals($role->getId(),$result[$i]['id']); + } + } +} diff --git a/tests/DatabaseTests/UserDbTest.php b/tests/DatabaseTests/UserDbTest.php new file mode 100644 index 0000000..43289a6 --- /dev/null +++ b/tests/DatabaseTests/UserDbTest.php @@ -0,0 +1,34 @@ +setName('Test User'); + try { + $user->save(); + $id = $user->getId(); + $this->assertGreaterThan(0,$id); + } + catch (Exception $e) { + $this->fail($e->getMessage()); + } + + $user = new User($id); + $this->assertEquals($user->getName(),'Test User'); + + $user->setName('Test'); + $user->save(); + + $user = new User($id); + $this->assertEquals($user->getName(),'Test'); + } +} diff --git a/tests/DatabaseTests/UserListDbTest.php b/tests/DatabaseTests/UserListDbTest.php new file mode 100644 index 0000000..1b6370b --- /dev/null +++ b/tests/DatabaseTests/UserListDbTest.php @@ -0,0 +1,30 @@ +query('select id from users order by username'); + $result = $query->fetchAll(); + + $list = new UserList(); + $list->find(); + $this->assertEquals($list->getSort(),'username'); + + foreach ($list as $i=>$user) + { + $this->assertEquals($user->getId(),$result[$i]['id']); + } + } +} diff --git a/tests/UnitTests.php b/tests/UnitTests.php new file mode 100644 index 0000000..42c96cc --- /dev/null +++ b/tests/UnitTests.php @@ -0,0 +1,18 @@ +addTestSuite('RoleUnitTest'); + $suite->addTestSuite('UserUnitTest'); + + return $suite; + } +} diff --git a/tests/UnitTests/RoleUnitTest.php b/tests/UnitTests/RoleUnitTest.php new file mode 100644 index 0000000..2fc2e07 --- /dev/null +++ b/tests/UnitTests/RoleUnitTest.php @@ -0,0 +1,25 @@ +validate(); + $this->fail('Missing name failed to throw exception'); + } + catch (Exception $e) { } + + $role->setName('Test Role'); + $role->validate(); + } + + public function testToString() + { + $role = new Role(); + $role->setName('Test Role'); + $this->assertEquals((string) $role,'Test Role'); + } +} diff --git a/tests/UnitTests/UserUnitTest.php b/tests/UnitTests/UserUnitTest.php new file mode 100644 index 0000000..396b9b3 --- /dev/null +++ b/tests/UnitTests/UserUnitTest.php @@ -0,0 +1,22 @@ +validate(); + $this->fail('Empty User failed to throw validation exception'); + } + catch (Exception $e) { } + + $user->setUsername('test'); + $user->setFirstname('Test'); + $user->setLastname('User'); + + $user->validate(); + } +}