From: Antony Lesuisse Date: Sat, 6 Oct 2012 17:10:53 +0000 (+0200) Subject: rm files, move postload hook X-Git-Tag: 7.0-server~193^2~195^2~3 X-Git-Url: http://git.inspyration.org/?a=commitdiff_plain;h=bc3cf0b74ff033d247a826ba55b608922454548a;p=odoo%2Fodoo.git rm files, move postload hook bzr revid: al@openerp.com-20121006171053-qggp1r5263h7v7vd --- diff --git a/LICENSE.web b/LICENSE.web deleted file mode 100644 index dba13ed..0000000 --- a/LICENSE.web +++ /dev/null @@ -1,661 +0,0 @@ - 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/Makefile b/Makefile deleted file mode 100644 index 4beb20d..0000000 --- a/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all doc release clean - -HOST = 127.0.0.1 -PORT = 8080 - -all: run - -run: - python openerp-web.py -a ${HOST} -p ${PORT} - -release: - python setup.py sdist - -install: - python setup.py install - -clean: - @find . -name '*.pyc' -exec rm -f {} + - @find . -name '*.pyo' -exec rm -f {} + - @find . -name '*.swp' -exec rm -f {} + - @find . -name '*~' -exec rm -f {} + - @rm -rf build - @rm -rf dist - @rm -rf *.egg-info - -doc: - make -C doc html - -cloc: - cloc addons/*/common/*.py addons/*/controllers/*.py addons/*/static/src/*.js addons/*/static/src/js/*.js addons/*/static/src/css/*.css addons/*/static/src/xml/*.xml - -blamestat: - echo addons/*/common/*.py addons/*/controllers/*.py addons/*/static/src/js/*.js addons/*/static/src/css/*.css addons/*/static/src/xml/*.xml | xargs -t -n 1 bzr blame -v --long --all | awk '{print $2}' | sort | uniq -c | sort -n - diff --git a/README.web b/README.web deleted file mode 100644 index 72393ba..0000000 --- a/README.web +++ /dev/null @@ -1,9 +0,0 @@ -OpenERP Web ------------ - -To build the documentation use: - -$ make doc - -then look at doc/build/html/index.html - diff --git a/addons/web/__init__.py b/addons/web/__init__.py index 621931e..c4337b3 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -1,31 +1,4 @@ -import logging - -from . import common -from . import controllers - -_logger = logging.getLogger(__name__) - -class Options(object): - pass - -def wsgi_postload(): - import openerp - import os - import tempfile - import getpass - _logger.info("embedded mode") - o = Options() - o.dbfilter = openerp.tools.config['dbfilter'] - o.server_wide_modules = openerp.conf.server_wide_modules or ['web'] - try: - username = getpass.getuser() - except Exception: - username = "unknown" - o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) - o.addons_path = openerp.modules.module.ad_paths - o.serve_static = True - o.backend = 'local' - - app = common.http.Root(o) - openerp.wsgi.register_wsgi_handler(app) +import common +import controllers +wsgi_postload = common.http.wsgi_postload diff --git a/addons/web/common/http.py b/addons/web/common/http.py index 4955876..3b9fdf3 100644 --- a/addons/web/common/http.py +++ b/addons/web/common/http.py @@ -579,4 +579,28 @@ class Root(object): ps = '/' return None +class Options(object): + pass + +def wsgi_postload(): + import openerp + import tempfile + import getpass + _logger.info("embedded mode") + o = Options() + o.dbfilter = openerp.tools.config['dbfilter'] + o.server_wide_modules = openerp.conf.server_wide_modules or ['web'] + try: + username = getpass.getuser() + except Exception: + username = "unknown" + o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) + o.addons_path = openerp.modules.module.ad_paths + o.serve_static = True + o.backend = 'local' + + app = Root(o) + openerp.wsgi.register_wsgi_handler(app) + + # vim:et:ts=4:sw=4: diff --git a/addons/web/doc/Makefile b/addons/web/doc/Makefile new file mode 100644 index 0000000..c1eff18 --- /dev/null +++ b/addons/web/doc/Makefile @@ -0,0 +1,154 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -q +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + sed -i '/-99999/d' _build/dirhtml/_static/flasky.css + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/addons/web/doc/_static/openerp.png b/addons/web/doc/_static/openerp.png new file mode 100644 index 0000000..d6dbd9d Binary files /dev/null and b/addons/web/doc/_static/openerp.png differ diff --git a/addons/web/doc/_templates/sidebarintro.html b/addons/web/doc/_templates/sidebarintro.html new file mode 100644 index 0000000..ecd5198 --- /dev/null +++ b/addons/web/doc/_templates/sidebarintro.html @@ -0,0 +1,16 @@ + + +

Other Docs

+ + +

Useful Links

+ diff --git a/addons/web/doc/_templates/sidebarlogo.html b/addons/web/doc/_templates/sidebarlogo.html new file mode 100644 index 0000000..de6e3e5 --- /dev/null +++ b/addons/web/doc/_templates/sidebarlogo.html @@ -0,0 +1,3 @@ + diff --git a/addons/web/doc/_themes/LICENSE b/addons/web/doc/_themes/LICENSE new file mode 100644 index 0000000..8daab7e --- /dev/null +++ b/addons/web/doc/_themes/LICENSE @@ -0,0 +1,37 @@ +Copyright (c) 2010 by Armin Ronacher. + +Some rights reserved. + +Redistribution and use in source and binary forms of the theme, 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. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +We kindly ask you to only use these themes in an unmodified manner just +for Flask and Flask-related products, not for unrelated projects. If you +like the visual style and want to use it for your own projects, please +consider making some larger changes to the themes (such as changing +font faces, sizes, colors or margins). + +THIS THEME 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 THEME, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/addons/web/doc/_themes/README b/addons/web/doc/_themes/README new file mode 100644 index 0000000..b3292bd --- /dev/null +++ b/addons/web/doc/_themes/README @@ -0,0 +1,31 @@ +Flask Sphinx Styles +=================== + +This repository contains sphinx styles for Flask and Flask related +projects. To use this style in your Sphinx documentation, follow +this guide: + +1. put this folder as _themes into your docs folder. Alternatively + you can also use git submodules to check out the contents there. +2. add this to your conf.py: + + sys.path.append(os.path.abspath('_themes')) + html_theme_path = ['_themes'] + html_theme = 'flask' + +The following themes exist: + +- 'flask' - the standard flask documentation theme for large + projects +- 'flask_small' - small one-page theme. Intended to be used by + very small addon libraries for flask. + +The following options exist for the flask_small theme: + + [options] + index_logo = '' filename of a picture in _static + to be used as replacement for the + h1 in the index.rst file. + index_logo_height = 120px height of the index logo + github_fork = '' repository name on github for the + "fork me" badge diff --git a/addons/web/doc/_themes/flask/layout.html b/addons/web/doc/_themes/flask/layout.html new file mode 100644 index 0000000..ad08ecc --- /dev/null +++ b/addons/web/doc/_themes/flask/layout.html @@ -0,0 +1,25 @@ +{%- extends "basic/layout.html" %} +{%- block extrahead %} + {{ super() }} + {% if theme_touch_icon %} + + {% endif %} + +{% endblock %} +{%- block relbar2 %}{% endblock %} +{% block header %} + {{ super() }} + {% if pagename == 'index' %} +
+ {% endif %} +{% endblock %} +{%- block footer %} + + {% if pagename == 'index' %} +
+ {% endif %} +{%- endblock %} diff --git a/addons/web/doc/_themes/flask/relations.html b/addons/web/doc/_themes/flask/relations.html new file mode 100644 index 0000000..3bbcde8 --- /dev/null +++ b/addons/web/doc/_themes/flask/relations.html @@ -0,0 +1,19 @@ +

Related Topics

+ diff --git a/addons/web/doc/_themes/flask/static/flasky.css_t b/addons/web/doc/_themes/flask/static/flasky.css_t new file mode 100644 index 0000000..0280e15 --- /dev/null +++ b/addons/web/doc/_themes/flask/static/flasky.css_t @@ -0,0 +1,395 @@ +/* + * flasky.css_t + * ~~~~~~~~~~~~ + * + * :copyright: Copyright 2010 by Armin Ronacher. + * :license: Flask Design License, see LICENSE for details. + */ + +{% set page_width = '80em' %} +{% set sidebar_width = '16em' %} + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Georgia', serif; + font-size: 15px; + background-color: white; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + width: {{ page_width }}; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 {{ sidebar_width }}; +} + +div.sphinxsidebar { + width: {{ sidebar_width }}; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #ffffff; + color: #3E4349; + padding: 0 0px 0 0px; +} + +img.floatingflask { + padding: 0 0 10px 10px; + float: right; +} + +div.footer { + width: {{ page_width }}; + margin: 20px auto 30px auto; + font-size: 12px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +div.related { + display: none; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebar { + font-size: 12px; + line-height: 1.5; +} + +div.sphinxsidebarwrapper { + padding: 0px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0 0 20px 0; + margin: 0; + text-align: center; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: 'Garamond', 'Georgia', serif; + color: #444; + font-size: 22px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 18px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar input { + border: 1px solid #ccc; + font-family: 'Georgia', serif; + font-size: 1em; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +{% if theme_index_logo %} +div.indexwrapper h1 { + text-indent: -999999px; + background: url({{ theme_index_logo }}) no-repeat center center; + height: {{ theme_index_logo_height }}; +} +{% endif %} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #ddd; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #eaeaea; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + background: #fafafa; + margin: 20px -30px; + padding: 10px 30px; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + +div.admonition tt.xref, div.admonition a tt { + border-bottom: 1px solid #fafafa; +} + +dd div.admonition { + margin-left: -60px; + padding-left: 60px; +} + +div.admonition p.admonition-title { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + font-size: 22px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: white; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt { + font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +img.screenshot { +} + +tt.descname, tt.descclassname { + font-size: 0.95em; +} + +tt.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #eee; + background: #fdfdfd; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.footnote td.label { + width: 0px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #eee; + padding: 7px 30px; + margin: 15px -30px; + line-height: 1.3em; +} + +dl pre, blockquote pre, li pre { + margin-left: -60px; + padding-left: 60px; +} + +dl dl pre { + margin-left: -90px; + padding-left: 90px; +} + +tt { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid white; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt { + background: #EEE; +} diff --git a/addons/web/doc/_themes/flask/static/small_flask.css b/addons/web/doc/_themes/flask/static/small_flask.css new file mode 100644 index 0000000..1c6df30 --- /dev/null +++ b/addons/web/doc/_themes/flask/static/small_flask.css @@ -0,0 +1,70 @@ +/* + * small_flask.css_t + * ~~~~~~~~~~~~~~~~~ + * + * :copyright: Copyright 2010 by Armin Ronacher. + * :license: Flask Design License, see LICENSE for details. + */ + +body { + margin: 0; + padding: 20px 30px; +} + +div.documentwrapper { + float: none; + background: white; +} + +div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: white; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, +div.sphinxsidebar h3 a { + color: white; +} + +div.sphinxsidebar a { + color: #aaa; +} + +div.sphinxsidebar p.logo { + display: none; +} + +div.document { + width: 100%; + margin: 0; +} + +div.related { + display: block; + margin: 0; + padding: 10px 0 20px 0; +} + +div.related ul, +div.related ul li { + margin: 0; + padding: 0; +} + +div.footer { + display: none; +} + +div.bodywrapper { + margin: 0; +} + +div.body { + min-height: 0; + padding: 0; +} diff --git a/addons/web/doc/_themes/flask/theme.conf b/addons/web/doc/_themes/flask/theme.conf new file mode 100644 index 0000000..18c720f --- /dev/null +++ b/addons/web/doc/_themes/flask/theme.conf @@ -0,0 +1,9 @@ +[theme] +inherit = basic +stylesheet = flasky.css +pygments_style = flask_theme_support.FlaskyStyle + +[options] +index_logo = '' +index_logo_height = 120px +touch_icon = diff --git a/addons/web/doc/_themes/flask_small/layout.html b/addons/web/doc/_themes/flask_small/layout.html new file mode 100644 index 0000000..aa1716a --- /dev/null +++ b/addons/web/doc/_themes/flask_small/layout.html @@ -0,0 +1,22 @@ +{% extends "basic/layout.html" %} +{% block header %} + {{ super() }} + {% if pagename == 'index' %} +
+ {% endif %} +{% endblock %} +{% block footer %} + {% if pagename == 'index' %} +
+ {% endif %} +{% endblock %} +{# do not display relbars #} +{% block relbar1 %}{% endblock %} +{% block relbar2 %} + {% if theme_github_fork %} + Fork me on GitHub + {% endif %} +{% endblock %} +{% block sidebar1 %}{% endblock %} +{% block sidebar2 %}{% endblock %} diff --git a/addons/web/doc/_themes/flask_small/static/flasky.css_t b/addons/web/doc/_themes/flask_small/static/flasky.css_t new file mode 100644 index 0000000..fe2141c --- /dev/null +++ b/addons/web/doc/_themes/flask_small/static/flasky.css_t @@ -0,0 +1,287 @@ +/* + * flasky.css_t + * ~~~~~~~~~~~~ + * + * Sphinx stylesheet -- flasky theme based on nature theme. + * + * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Georgia', serif; + font-size: 17px; + color: #000; + background: white; + margin: 0; + padding: 0; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 40px auto 0 auto; + width: 700px; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #ffffff; + color: #3E4349; + padding: 0 30px 30px 30px; +} + +img.floatingflask { + padding: 0 0 10px 10px; + float: right; +} + +div.footer { + text-align: right; + color: #888; + padding: 10px; + font-size: 14px; + width: 650px; + margin: 0 auto 40px auto; +} + +div.footer a { + color: #888; + text-decoration: underline; +} + +div.related { + line-height: 32px; + color: #888; +} + +div.related ul { + padding: 0 0 0 10px; +} + +div.related a { + color: #444; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body { + padding-bottom: 40px; /* saved for footer */ +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +{% if theme_index_logo %} +div.indexwrapper h1 { + text-indent: -999999px; + background: url({{ theme_index_logo }}) no-repeat center center; + height: {{ theme_index_logo_height }}; +} +{% endif %} + +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: white; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #eaeaea; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + background: #fafafa; + margin: 20px -30px; + padding: 10px 30px; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + +div.admonition p.admonition-title { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight{ + background-color: white; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt { + font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.85em; +} + +img.screenshot { +} + +tt.descname, tt.descclassname { + font-size: 0.95em; +} + +tt.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #eee; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.footnote td { + padding: 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +pre { + padding: 0; + margin: 15px -30px; + padding: 8px; + line-height: 1.3em; + padding: 7px 30px; + background: #eee; + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; +} + +dl pre { + margin-left: -60px; + padding-left: 60px; +} + +tt { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, a tt { + background-color: #FBFBFB; +} + +a:hover tt { + background: #EEE; +} diff --git a/addons/web/doc/_themes/flask_small/theme.conf b/addons/web/doc/_themes/flask_small/theme.conf new file mode 100644 index 0000000..542b462 --- /dev/null +++ b/addons/web/doc/_themes/flask_small/theme.conf @@ -0,0 +1,10 @@ +[theme] +inherit = basic +stylesheet = flasky.css +nosidebar = true +pygments_style = flask_theme_support.FlaskyStyle + +[options] +index_logo = '' +index_logo_height = 120px +github_fork = '' diff --git a/addons/web/doc/_themes/flask_theme_support.py b/addons/web/doc/_themes/flask_theme_support.py new file mode 100644 index 0000000..33f4744 --- /dev/null +++ b/addons/web/doc/_themes/flask_theme_support.py @@ -0,0 +1,86 @@ +# flasky extensions. flasky pygments style based on tango style +from pygments.style import Style +from pygments.token import Keyword, Name, Comment, String, Error, \ + Number, Operator, Generic, Whitespace, Punctuation, Other, Literal + + +class FlaskyStyle(Style): + background_color = "#f8f8f8" + default_style = "" + + styles = { + # No corresponding class for the following: + #Text: "", # class: '' + Whitespace: "underline #f8f8f8", # class: 'w' + Error: "#a40000 border:#ef2929", # class: 'err' + Other: "#000000", # class 'x' + + Comment: "italic #8f5902", # class: 'c' + Comment.Preproc: "noitalic", # class: 'cp' + + Keyword: "bold #004461", # class: 'k' + Keyword.Constant: "bold #004461", # class: 'kc' + Keyword.Declaration: "bold #004461", # class: 'kd' + Keyword.Namespace: "bold #004461", # class: 'kn' + Keyword.Pseudo: "bold #004461", # class: 'kp' + Keyword.Reserved: "bold #004461", # class: 'kr' + Keyword.Type: "bold #004461", # class: 'kt' + + Operator: "#582800", # class: 'o' + Operator.Word: "bold #004461", # class: 'ow' - like keywords + + Punctuation: "bold #000000", # class: 'p' + + # because special names such as Name.Class, Name.Function, etc. + # are not recognized as such later in the parsing, we choose them + # to look the same as ordinary variables. + Name: "#000000", # class: 'n' + Name.Attribute: "#c4a000", # class: 'na' - to be revised + Name.Builtin: "#004461", # class: 'nb' + Name.Builtin.Pseudo: "#3465a4", # class: 'bp' + Name.Class: "#000000", # class: 'nc' - to be revised + Name.Constant: "#000000", # class: 'no' - to be revised + Name.Decorator: "#888", # class: 'nd' - to be revised + Name.Entity: "#ce5c00", # class: 'ni' + Name.Exception: "bold #cc0000", # class: 'ne' + Name.Function: "#000000", # class: 'nf' + Name.Property: "#000000", # class: 'py' + Name.Label: "#f57900", # class: 'nl' + Name.Namespace: "#000000", # class: 'nn' - to be revised + Name.Other: "#000000", # class: 'nx' + Name.Tag: "bold #004461", # class: 'nt' - like a keyword + Name.Variable: "#000000", # class: 'nv' - to be revised + Name.Variable.Class: "#000000", # class: 'vc' - to be revised + Name.Variable.Global: "#000000", # class: 'vg' - to be revised + Name.Variable.Instance: "#000000", # class: 'vi' - to be revised + + Number: "#990000", # class: 'm' + + Literal: "#000000", # class: 'l' + Literal.Date: "#000000", # class: 'ld' + + String: "#4e9a06", # class: 's' + String.Backtick: "#4e9a06", # class: 'sb' + String.Char: "#4e9a06", # class: 'sc' + String.Doc: "italic #8f5902", # class: 'sd' - like a comment + String.Double: "#4e9a06", # class: 's2' + String.Escape: "#4e9a06", # class: 'se' + String.Heredoc: "#4e9a06", # class: 'sh' + String.Interpol: "#4e9a06", # class: 'si' + String.Other: "#4e9a06", # class: 'sx' + String.Regex: "#4e9a06", # class: 'sr' + String.Single: "#4e9a06", # class: 's1' + String.Symbol: "#4e9a06", # class: 'ss' + + Generic: "#000000", # class: 'g' + Generic.Deleted: "#a40000", # class: 'gd' + Generic.Emph: "italic #000000", # class: 'ge' + Generic.Error: "#ef2929", # class: 'gr' + Generic.Heading: "bold #000080", # class: 'gh' + Generic.Inserted: "#00A000", # class: 'gi' + Generic.Output: "#888", # class: 'go' + Generic.Prompt: "#745334", # class: 'gp' + Generic.Strong: "bold #000000", # class: 'gs' + Generic.Subheading: "bold #800080", # class: 'gu' + Generic.Traceback: "bold #a40000", # class: 'gt' + } diff --git a/addons/web/doc/addon-structure.txt b/addons/web/doc/addon-structure.txt new file mode 100644 index 0000000..3815174 --- /dev/null +++ b/addons/web/doc/addon-structure.txt @@ -0,0 +1,12 @@ + + +-- __openerp__.py + +-- controllers/ + +-- static/ + +-- lib/ + +-- src/ + +-- css/ + +-- img/ + +-- js/ + +-- xml/ + +-- test/ + +-- test/ diff --git a/addons/web/doc/async.rst b/addons/web/doc/async.rst new file mode 100644 index 0000000..23b3409 --- /dev/null +++ b/addons/web/doc/async.rst @@ -0,0 +1,353 @@ +Don't stop the world now: asynchronous development and Javascript +================================================================= + +As a language (and runtime), javascript is fundamentally +single-threaded. This means any blocking request or computation will +blocks the whole page (and, in older browsers, the software itself +even preventing users from switching to an other tab): a javascript +environment can be seen as an event-based runloop where application +developers have no control over the runloop itself. + +As a result, performing long-running synchronous network requests or +other types of complex and expensive accesses is frowned upon and +asynchronous APIs are used instead. + +Asynchronous code rarely comes naturally, especially for developers +used to synchronous server-side code (in Python, Java or C#) where the +code will just block until the deed is gone. This is increased further +when asynchronous programming is not a first-class concept and is +instead implemented on top of callbacks-based programming, which is +the case in javascript. + +The goal of this guide is to provide some tools to deal with +asynchronous systems, and warn against systematic issues or dangers. + +Deferreds +--------- + +Deferreds are a form of `promises`_. OpenERP Web currently uses +`jQuery's deferred`_, but any `CommonJS Promises/A`_ implementation +should work. + +The core idea of deferreds is that potentially asynchronous methods +will return a :js:class:`Deferred` object instead of an arbitrary +value or (most commonly) nothing. + +This object can then be used to track the end of the asynchronous +operation by adding callbacks onto it, either success callbacks or +error callbacks. + +A great advantage of deferreds over simply passing callback functions +directly to asynchronous methods is the ability to :ref:`compose them +`. + +Using deferreds +~~~~~~~~~~~~~~~ + +`CommonJS Promises/A`_ deferreds have only one method of importance: +:js:func:`Deferred.then`. This method is used to attach new callbacks +to the deferred object. + +* the first parameter attaches a success callback, called when the + deferred object is successfully resolved and provided with the + resolved value(s) for the asynchronous operation. + +* the second parameter attaches a failure callback, called when the + deferred object is rejected and provided with rejection values + (often some sort of error message). + +Callbacks attached to deferreds are never "lost": if a callback is +attached to an already resolved or rejected deferred, the callback +will be called (or ignored) immediately. A deferred is also only ever +resolved or rejected once, and is either resolved or rejected: a given +deferred can not call a single success callback twice, or call both a +success and a failure callbacks. + +:js:func:`~Deferred.then` should be the method you'll use most often +when interacting with deferred objects (and thus asynchronous APIs). + +Building deferreds +~~~~~~~~~~~~~~~~~~ + +After using asynchronous APIs may come the time to build them: for +`mocks`_, to compose deferreds from multiple source in a complex +manner, in order to let the current operations repaint the screen or +give other events the time to unfold, ... + +This is easy using jQuery's deferred objects. + +.. note:: this section is an implementation detail of jQuery Deferred + objects, the creation of promises is not part of any + standard (even tentative) that I know of. If you are using + deferred objects which are not jQuery's, their API may (and + often will) be completely different. + +Deferreds are created by invoking their constructor [#]_ without any +argument. This creates a :js:class:`Deferred` instance object with the +following methods: + +:js:func:`Deferred.resolve` + + As its name indicates, this method moves the deferred to the + "Resolved" state. It can be provided as many arguments as + necessary, these arguments will be provided to any pending success + callback. + +:js:func:`Deferred.reject` + + Similar to :js:func:`~Deferred.resolve`, but moves the deferred to + the "Rejected" state and calls pending failure handlers. + +:js:func:`Deferred.promise` + + Creates a readonly view of the deferred object. It is generally a + good idea to return a promise view of the deferred to prevent + callers from resolving or rejecting the deferred in your stead. + +:js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used +to inform callers that the asynchronous operation has failed (or +succeeded). These methods should simply be called when the +asynchronous operation has ended, to notify anybody interested in its +result(s). + +.. _deferred-composition: + +Composing deferreds +~~~~~~~~~~~~~~~~~~~ + +What we've seen so far is pretty nice, but mostly doable by passing +functions to other functions (well adding functions post-facto would +probably be a chore... still, doable). + +Deferreds truly shine when code needs to compose asynchronous +operations in some way or other, as they can be used as a basis for +such composition. + +There are two main forms of compositions over deferred: multiplexing +and piping/cascading. + +Deferred multiplexing +````````````````````` + +The most common reason for multiplexing deferred is simply performing +2+ asynchronous operations and wanting to wait until all of them are +done before moving on (and executing more stuff). + +The jQuery multiplexing function for promises is :js:func:`when`. + +.. note:: the multiplexing behavior of jQuery's :js:func:`when` is an + (incompatible, mostly) extension of the behavior defined in + `CommonJS Promises/B`_. + +This function can take any number of promises [#]_ and will return a +promise. + +This returned promise will be resolved when *all* multiplexed promises +are resolved, and will be rejected as soon as one of the multiplexed +promises is rejected (it behaves like Python's ``all()``, but with +promise objects instead of boolean-ish). + +The resolved values of the various promises multiplexed via +:js:func:`when` are mapped to the arguments of :js:func:`when`'s +success callback, if they are needed. The resolved values of a promise +are at the same index in the callback's arguments as the promise in +the :js:func:`when` call so you will have: + +.. code-block:: javascript + + $.when(p0, p1, p2, p3).then( + function (results0, results1, results2, results3) { + // code + }); + +.. warning:: + + in a normal mapping, each parameter to the callback would be an + array: each promise is conceptually resolved with an array of 0..n + values and these values are passed to :js:func:`when`'s + callback. But jQuery treats deferreds resolving a single value + specially, and "unwraps" that value. + + For instance, in the code block above if the index of each promise + is the number of values it resolves (0 to 3), ``results0`` is an + empty array, ``results2`` is an array of 2 elements (a pair) but + ``results1`` is the actual value resolved by ``p1``, not an array. + +Deferred chaining +````````````````` + +A second useful composition is starting an asynchronous operation as +the result of an other asynchronous operation, and wanting the result +of both: :js:func:`Deferred.then` returns the deferred on which it was +called, so handle e.g. OpenERP's search/read sequence with this would +require something along the lines of: + +.. code-block:: javascript + + var result = $.Deferred(); + Model.search(condition).then(function (ids) { + Model.read(ids, fields).then(function (records) { + result.resolve(records); + }); + }); + return result.promise(); + +While it doesn't look too bad for trivial code, this quickly gets +unwieldy. + +Instead, jQuery provides a tool to handle this kind of chains: +:js:func:`Deferred.pipe`. + +:js:func:`~Deferred.pipe` has the same signature as +:js:func:`~Deferred.then` and could be used in the same manner +provided its return value was not used. + +It differs from :js:func:`~Deferred.then` in two ways: it returns a +new promise object, not the one it was called with, and the return +values of the callbacks is actually important to it: whichever +callback is called, + +* If the callback is not set (not provided or left to null), the + resolution or rejection value(s) is simply forwarded to + :js:func:`~Deferred.pipe`'s promise (it's essentially a noop) + +* If the callback is set and does not return an observable object (a + deferred or a promise), the value it returns (``undefined`` if it + does not return anything) will replace the value it was given, e.g. + + .. code-block:: javascript + + promise.pipe(function () { + console.log('called'); + }); + + will resolve with the sole value ``undefined``. + +* If the callback is set and returns an observable object, that object + will be the actual resolution (and result) of the pipe. This means a + resolved promise from the failure callback will resolve the pipe, + and a failure promise from the success callback will reject the + pipe. + + This provides an easy way to chain operation successes, and the + previous piece of code can now be rewritten: + + .. code-block:: javascript + + return Model.search(condition).pipe(function (ids) { + return Model.read(ids, fields); + }); + + the result of the whole expression will encode failure if either + ``search`` or ``read`` fails (with the right rejection values), and + will be resolved with ``read``'s resolution values if the chain + executes correctly. + +:js:func:`~Deferred.pipe` is also useful to adapt third-party +promise-based APIs, in order to filter their resolution value counts +for instance (to take advantage of :js:func:`when` 's special treatment +of single-value promises). + +jQuery.Deferred API +~~~~~~~~~~~~~~~~~~~ + +.. js:function:: when(deferreds…) + + :param deferreds: deferred objects to multiplex + :returns: a multiplexed deferred + :rtype: :js:class:`Deferred` + +.. js:class:: Deferred + + .. js:function:: Deferred.then(doneCallback[, failCallback]) + + Attaches new callbacks to the resolution or rejection of the + deferred object. Callbacks are executed in the order they are + attached to the deferred. + + To provide only a failure callback, pass ``null`` as the + ``doneCallback``, to provide only a success callback the + second argument can just be ignored (and not passed at all). + + :param doneCallback: function called when the deferred is resolved + :type doneCallback: Function + :param failCallback: function called when the deferred is rejected + :type failCallback: Function + :returns: the deferred object on which it was called + :rtype: :js:class:`Deferred` + + .. js:function:: Deferred.done(doneCallback) + + Attaches a new success callback to the deferred, shortcut for + ``deferred.then(doneCallback)``. + + This is a jQuery extension to `CommonJS Promises/A`_ providing + little value over calling :js:func:`~Deferred.then` directly, + it should be avoided. + + :param doneCallback: function called when the deferred is resolved + :type doneCallback: Function + :returns: the deferred object on which it was called + :rtype: :js:class:`Deferred` + + .. js:function:: Deferred.fail(failCallback) + + Attaches a new failure callback to the deferred, shortcut for + ``deferred.then(null, failCallback)``. + + A second jQuery extension to `Promises/A `_. Although it provides more value than + :js:func:`~Deferred.done`, it still is not much and should be + avoided as well. + + :param failCallback: function called when the deferred is rejected + :type failCallback: Function + :returns: the deferred object on which it was called + :rtype: :js:class:`Deferred` + + .. js:function:: Deferred.promise() + + Returns a read-only view of the deferred object, with all + mutators (resolve and reject) methods removed. + + .. js:function:: Deferred.resolve(value…) + + Called to resolve a deferred, any value provided will be + passed onto the success handlers of the deferred object. + + Resolving a deferred which has already been resolved or + rejected has no effect. + + .. js:function:: Deferred.reject(value…) + + Called to reject (fail) a deferred, any value provided will be + passed onto the failure handler of the deferred object. + + Rejecting a deferred which has already been resolved or + rejected has no effect. + + .. js:function:: Deferred.pipe(doneFilter[, failFilter]) + + Filters the result of a deferred, able to transform a success + into failure and a failure into success, or to delay + resolution further. + +.. [#] or simply calling :js:class:`Deferred` as a function, the + result is the same + +.. [#] or not-promises, the `CommonJS Promises/B`_ role of + :js:func:`when` is to be able to treat values and promises + uniformly: :js:func:`when` will pass promises through directly, + but non-promise values and objects will be transformed into a + resolved promise (resolving themselves with the value itself). + + jQuery's :js:func:`when` keeps this behavior making deferreds + easy to build from "static" values, or allowing defensive code + where expected promises are wrapped in :js:func:`when` just in + case. + +.. _promises: http://en.wikipedia.org/wiki/Promise_(programming) +.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/ +.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A +.. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B +.. _mocks: http://en.wikipedia.org/wiki/Mock_object diff --git a/addons/web/doc/changelog-7.0.rst b/addons/web/doc/changelog-7.0.rst new file mode 100644 index 0000000..00d0236 --- /dev/null +++ b/addons/web/doc/changelog-7.0.rst @@ -0,0 +1,108 @@ +API changes from OpenERP Web 6.1 to 7.0 +======================================= + +DataSet -> Model +---------------- + +The 6.1 ``DataSet`` API has been deprecated in favor of the smaller +and more orthogonal :doc:`Model ` API, which more closely +matches the API in OpenERP Web's Python side and in OpenObject addons +and removes most stateful behavior of DataSet. + +Migration guide +~~~~~~~~~~~~~~~ + +* Actual arbitrary RPC calls can just be remapped on a + :js:class:`~openerp.web.Model` instance: + + .. code-block:: javascript + + dataset.call(method, args) + + or + + .. code-block:: javascript + + dataset.call_and_eval(method, args) + + can be replaced by calls to :js:func:`openerp.web.Model.call`: + + .. code-block:: javascript + + model.call(method, args) + + If callbacks are passed directly to the older methods, they need to + be added to the new one via ``.then()``. + + .. note:: + + The ``context_index`` and ``domain_index`` features were not + ported, context and domain now need to be passed in "in full", + they won't be automatically filled with the user's current + context. + +* Shorcut methods (``name_get``, ``name_search``, ``unlink``, + ``write``, ...) should be ported to + :js:func:`openerp.web.Model.call`, using the server's original + signature. On the other hand, the non-shortcut equivalents can now + use keyword arguments (see :js:func:`~openerp.web.Model.call`'s + signature for details) + +* ``read_slice``, which allowed a single round-trip to perform a + search and a read, should be reimplemented via + :js:class:`~openerp.web.Query` objects (see: + :js:func:`~openerp.web.Model.query`) for clearer and simpler + code. ``read_index`` should be replaced by a + :js:class:`~openerp.web.Query` as well, combining + :js:func:`~openerp.web.Query.offset` and + :js:func:`~openerp.web.Query.first`. + +Rationale +~~~~~~~~~ + +Renaming + + The name *DataSet* exists in the CS community consciousness, and + (as its name implies) it's a set of data (often fetched from a + database, maybe lazily). OpenERP Web's dataset behaves very + differently as it does not store (much) data (only a bunch of ids + and just enough state to break things). The name "Model" matches + the one used on the Python side for the task of building an RPC + proxy to OpenERP objects. + +API simplification + + ``DataSet`` has a number of methods which serve as little more + than shortcuts, or are there due to domain and context evaluation + issues in 6.1. + + The shortcuts really add little value, and OpenERP Web 6.2 embeds + a restricted Python evaluator (in javascript) meaning most of the + context and domain parsing & evaluation can be moved to the + javascript code and does not require cooperative RPC bridging. + +DataGroup -> also Model +----------------------- + +Alongside the deprecation of ``DataSet`` for +:js:class:`~openerp.web.Model`, OpenERP Web 7.0 also deprecates +``DataGroup`` and its subtypes in favor of a single method on +:js:class:`~openerp.web.Query`: +:js:func:`~openerp.web.Query.group_by`. + +Migration guide +~~~~~~~~~~~~~~~ + +Rationale +~~~~~~~~~ + +While the ``DataGroup`` API worked (mostly), it is quite odd and +alien-looking, a bit too Smalltalk-inspired (behaves like a +self-contained flow-control structure for reasons which may or may not +have been good). + +Because it is heavily related to ``DataSet`` (as it *yields* +``DataSet`` objects), deprecating ``DataSet`` automatically deprecates +``DataGroup`` (if we want to stay consistent), which is a good time to +make the API more imperative and look more like what most developers +are used to. diff --git a/addons/web/doc/conf.py b/addons/web/doc/conf.py new file mode 100644 index 0000000..83fc969 --- /dev/null +++ b/addons/web/doc/conf.py @@ -0,0 +1,257 @@ +# -*- coding: utf-8 -*- +# +# OpenERP Technical Documentation configuration file, created by +# sphinx-quickstart on Fri Feb 17 16:14:06 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.append(os.path.abspath('_themes')) +sys.path.insert(0, os.path.abspath('../addons')) +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'OpenERP Web Developers Documentation' +copyright = u'2012, OpenERP s.a.' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '7.0' +# The full version, including alpha/beta/rc tags. +release = '7.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'flask' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['_themes'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], + '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', + 'sourcelink.html', 'searchbox.html'] +} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'openerp-web-doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'openerp-web-doc.tex', u'OpenERP Web Developers Documentation', + u'OpenERP s.a.', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'openerp-web-doc', u'OpenERP Web Developers Documentation', + [u'OpenERP s.a.'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'OpenERPWebDocumentation', u'OpenERP Web Developers Documentation', + u'OpenERP s.a.', 'OpenERPWebDocumentation', 'Developers documentation for the openerp-web project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +todo_include_todos = True + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + 'python': ('http://docs.python.org/', None), + 'openerpserver': ('http://doc.openerp.com/trunk/developers/server', None), + 'openerpdev': ('http://doc.openerp.com/trunk/developers', None), +} diff --git a/addons/web/doc/form-notes.rst b/addons/web/doc/form-notes.rst new file mode 100644 index 0000000..98d4a4d --- /dev/null +++ b/addons/web/doc/form-notes.rst @@ -0,0 +1,55 @@ +Notes on the usage of the Form View as a sub-widget +=================================================== + +Undocumented stuff +------------------ + +* ``initial_mode`` *option* defines the starting mode of the form + view, one of ``view`` and ``edit`` (?). Default value is ``view`` + (non-editable form). + +* ``embedded_view`` *attribute* has to be set separately when + providing a view directly, no option available for that usage. + + * View arch **must** contain node with + ``@class="oe_form_container"``, otherwise everything will break + without any info + + * Root element of view arch not being ``form`` may or may not work + correctly, no idea. + + * Freeform views => ``@version="7.0"`` + +* Form is not entirely loaded (some widgets may not appear) unless + ``on_record_loaded`` is called (or ``do_show``, which itself calls + ``on_record_loaded``). + +* "Empty" form => ``on_button_new`` (...), or manually call + ``default_get`` + ``on_record_loaded`` + +* Form fields default to width: 100%, padding, !important margin, can + be reached via ``.oe_form_field`` + +* Form *will* render buttons and a pager, offers options to locate + both outside of form itself (``$buttons`` and ``$pager``), providing + empty jquery objects (``$()``) seems to stop displaying both but not + sure if there are deleterious side-effects. + + Other options: + + * Pass in ``$(document.createDocumentFragment)`` to ensure it's a + DOM-compatible tree completely outside of the actual DOM. + + * ??? + +* readonly fields probably don't have a background, beware if need of + overlay + + * What is the difference between ``readonly`` and + ``effective_readonly``? + +* No facilities for DOM events handling/delegations e.g. handling + keyup/keydown/keypress from a form fields into the form's user. + + * Also no way to reverse from a DOM node (e.g. DOMEvent#target) back to a + form view field easily diff --git a/addons/web/doc/guides/client-action.rst b/addons/web/doc/guides/client-action.rst new file mode 100644 index 0000000..30f0c7e --- /dev/null +++ b/addons/web/doc/guides/client-action.rst @@ -0,0 +1,112 @@ +.. highlight:: javascript + +Creating a new client action +============================ + +Client actions are the client-side version of OpenERP's "Server +Actions": instead of allowing for semi-arbitrary code to be executed +in the server, they allow for execution of client-customized code. + +On the server side, a client action is an action of type +``ir.actions.client``, which has (at most) two properties: a mandatory +``tag``, which is an arbitrary string by which the client will +identify the action, and an optional ``params`` which is simply a map +of keys and values sent to the client as-is (this way, client actions +can be made generic and reused in multiple contexts). + +General Structure +----------------- + +In the OpenERP Web code, a client action only requires two pieces of +information: + +* Mapping the action's ``tag`` to an object + +* Providing said object. Two different types of objects can be mapped + to a client action: + + * An OpenERP Web widget, which must inherit from + :js:class:`openerp.web.Widget` + + * A regular javascript function + +The major difference is in the lifecycle of these: + +* if the client action maps to a function, the function will simply be + called when executing the action. The function can have no further + interaction with the Web Client itself, although it can return an + action which will be executed after it. + +* if, on the other hand, the client action maps to a + :js:class:`~openerp.web.Widget`, that + :js:class:`~openerp.web.Widget` will be instantiated and added to + the web client's canvas, with the usual + :js:class:`~openerp.web.Widget` lifecycle (essentially, it will + either take over the content area of the client or it will be + integrated within a dialog). + +For example, to create a client action displaying a ``res.widget`` +object:: + + // Registers the object 'openerp.web_dashboard.Widget' to the client + // action tag 'board.home.widgets' + instance.web.client_actions.add( + 'board.home.widgets', 'openerp.web_dashboard.Widget'); + instance.web_dashboard.Widget = instance.web.Widget.extend({ + template: 'HomeWidget' + }); + +At this point, the generic :js:class:`~openerp.web.Widget` lifecycle +takes over, the template is rendered, inserted in the client DOM, +bound on the object's ``$el`` property and the object is started. + +If the client action takes parameters, these parameters are passed in as a +second positional parameter to the constructor:: + + init: function (parent, params) { + // execute the Widget's init + this._super(parent); + // board.home.widgets only takes a single param, the identifier of the + // res.widget object it should display. Store it for later + this.widget_id = params.widget_id; + } + +More complex initialization (DOM manipulations, RPC requests, ...) +should be performed in the :js:func:`~openerp.web.Widget.start()` +method. + +.. note:: + + As required by :js:class:`~openerp.web.Widget`'s contract, if + :js:func:`~openerp.web.Widget.start()` executes any asynchronous + code it should return a ``$.Deferred`` so callers know when it's + ready for interaction. + + Although generally speaking client actions are not really + interacted with. + +.. code-block:: javascript + + start: function () { + return $.when( + this._super(), + // Simply read the res.widget object this action should display + new instance.web.Model('res.widget').call( + 'read', [[this.widget_id], ['title']]) + .then(this.proxy('on_widget_loaded')); + } + +The client action can then behave exactly as it wishes to within its +root (``this.$el``). In this case, it performs further renderings once +its widget's content is retrieved:: + + on_widget_loaded: function (widgets) { + var widget = widgets[0]; + var url = _.sprintf( + '/web_dashboard/widgets/content?session_id=%s&widget_id=%d', + this.session.session_id, widget.id); + this.$el.html(QWeb.render('HomeWidget.content', { + widget: widget, + url: url + })); + } diff --git a/addons/web/doc/index.rst b/addons/web/doc/index.rst new file mode 100644 index 0000000..6a0d8de --- /dev/null +++ b/addons/web/doc/index.rst @@ -0,0 +1,32 @@ +.. OpenERP Web documentation master file, created by + sphinx-quickstart on Fri Mar 18 16:31:55 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to OpenERP Web's documentation! +======================================= + +Contents: + +.. toctree:: + :maxdepth: 1 + + changelog-7.0 + + async + rpc + + widget + search-view + + list-view + form-notes + + guides/client-action + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/addons/web/doc/list-view.rst b/addons/web/doc/list-view.rst new file mode 100644 index 0000000..b7cdc1c --- /dev/null +++ b/addons/web/doc/list-view.rst @@ -0,0 +1,531 @@ +List View +========= + +Style Hooks +----------- + +The list view provides a few style hook classes for re-styling of list views in +various situations: + +``.oe_list`` + + The root element of the list view, styling rules should be rooted + on that class. + +``table.oe_list_content`` + + The root table for the listview, accessory components may be + generated or added outside this section, this is the list view + "proper". + +``.oe_list_buttons`` + + The action buttons array for the list view, with its sub-elements + + ``.oe_list_add`` + + The default "Create"/"Add" button of the list view + + ``.oe_alternative`` + + The "alternative choice" for the list view, by default text + along the lines of "or import" with a link. + +``.oe_list_field_cell`` + + The cell (``td``) for a given field of the list view, cells which + are *not* fields (e.g. name of a group, or number of items in a + group) will not have this class. The field cell can be further + specified: + + ``.oe_number`` + + Numeric cell types (integer and float) + + ``.oe_button`` + + Action button (``button`` tag in the view) inside the cell + + ``.oe_readonly`` + + Readonly field cell + + ``.oe_list_field_$type`` + + Additional class for the precise type of the cell, ``$type`` + is the field's @widget if there is one, otherwise it's the + field's type. + +``.oe_list_record_selector`` + + Selector cells + +Editable list view +++++++++++++++++++ + +The editable list view module adds a few supplementary style hook +classes, for edition situations: + +``.oe_list_editable`` + + Added to the ``.oe_list`` when the list is editable (however that + was done). The class may be removed on-the-fly if the list becomes + non-editable. + +``.oe_editing`` + + Added to both ``.oe_list`` and ``.oe_list_button`` (as the + buttons may be outside of the list view) when a row of the list is + currently being edited. + +``tr.oe_edition`` + + Class set on the row being edited itself. Note that the edition + form is *not* contained within the row, this allows for styling or + modifying the row while it's being edited separately. Mostly for + fields which can not be edited (e.g. read-only fields). + +Columns display customization +----------------------------- + +The list view provides a registry to +:js:class:`openerp.web.list.Column` objects allowing for the +customization of a column's display (e.g. so that a binary field is +rendered as a link to the binary file directly in the list view). + +The registry is ``instance.web.list.columns``, the keys are of the +form ``tag.type`` where ``tag`` can be ``field`` or ``button``, and +``type`` can be either the field's type or the field's ``@widget`` (in +the view). + +Most of the time, you'll want to define a ``tag.widget`` key +(e.g. ``field.progressbar``). + +.. js:class:: openerp.web.list.Column(id, tag, attrs) + + .. js:function:: openerp.web.list.Column.format(record_data, options) + + Top-level formatting method, returns an empty string if the + column is invisible (unless the ``process_modifiers=false`` + option is provided); returns ``options.value_if_empty`` or an + empty string if there is no value in the record for the + column. + + Otherwise calls :js:func:`~openerp.web.list.Column._format` + and returns its result. + + This method only needs to be overridden if the column has no + concept of values (and needs to bypass that check), for a + button for instance. + + Otherwise, custom columns should generally override + :js:func:`~openerp.web.list.Column._format` instead. + + :returns: String + + .. js:function:: openerp.web.list.Column._format(record_data, options) + + Never called directly, called if the column is visible and has + a value. + + The default implementation calls + :js:func:`~openerp.web.format_value` and htmlescapes the + result (via ``_.escape``). + + Note that the implementation of + :js:func:`~openerp.web.list.Column._format` *must* escape the + data provided to it, its output will *not* be escaped by + :js:func:`~openerp.web.list.Column.format`. + + :returns: String + +Editable list view +------------------ + +List view edition is an extension to the base listview providing the +capability of inline record edition by delegating to an embedded form +view. + +Editability status +++++++++++++++++++ + +The editability status of a list view can be queried through the +:js:func:`~openerp.web.ListView.editable` method, will return a falsy +value if the listview is not currently editable. + +The editability status is based on three flags: + +``tree/@editable`` + + If present, can be either ``"top"`` or ``"bottom"``. Either will + make the list view editable, with new records being respectively + created at the top or at the bottom of the view. + +``context.set_editable`` + + Boolean flag extracted from a search context (during the + :js:func:`~openerp.web.ListView.do_search`` handler), ``true`` + will make the view editable (from the top), ``false`` or the + absence of the flag is a noop. + +``defaults.editable`` + + Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or + ``"bottom"``, fallback for the list view if none of the previous + two flags are set. + +These three flags can only *make* a listview editable, they can *not* +override a previously set flag. To do that, a listview user should +instead cancel :ref:`the edit:before event `. + +The editable list view module adds a number of methods to the list +view, on top of implementing the :js:class:`EditorDelegate` protocol: + +Interaction Methods ++++++++++++++++++++ + +.. js:function:: openerp.web.ListView.ensure_saved + + Attempts to resolve the pending edition, if any, by saving the + edited row's current state. + + :returns: delegate resolving to all editions having been saved, or + rejected if a pending edition could not be saved + (e.g. validation failure) + +.. js:function:: openerp.web.ListView.start_edition([record][, options]) + + Starts editing the provided record inline, through an overlay form + view of editable fields in the record. + + If no record is provided, creates a new one according to the + editability configuration of the list view. + + This method resolves any pending edition when invoked, before + starting a new edition. + + :param record: record to edit, or null to create a new record + :type record: :js:class:`~openerp.web.list.Record` + :param EditOptions options: + :returns: delegate to the form used for the edition + +.. js:function:: openerp.web.ListView.save_edition + + Resolves the pending edition. + + :returns: delegate to the save being completed, resolves to an + object with two attributes ``created`` (flag indicating + whether the saved record was just created or was + updated) and ``record`` the reloaded record having been + edited. + +.. js:function:: openerp.web.ListView.cancel_edition([force=false]) + + Cancels pending edition, cleans up the list view in case of + creation (removes the empty record being created). + + :param Boolean force: doesn't check if the user has added any + data, discards the edition unconditionally + +Utility Methods ++++++++++++++++ + +.. js:function:: openerp.web.ListView.get_cells_for(row) + + Extracts the cells from a listview row, and puts them in a + {fieldname: cell} mapping for analysis and manipulation. + + :param jQuery row: + :rtype: Object + +.. js:function:: openerp.web.ListView.with_event(event_name, event, action[, args][, trigger_params]) + + Executes ``action`` in the context of the view's editor, + bracketing it with cancellable event signals. + + :param String event_name: base name for the bracketing event, will + be postfixed by ``:before`` and + ``:after`` before being called + (respectively before and after + ``action`` is executed) + :param Object event: object passed to the ``:before`` event + handlers. + :param Function action: function called with the view's editor as + its ``this``. May return a deferred. + :param Array args: arguments passed to ``action`` + :param Array trigger_params: arguments passed to the ``:after`` + event handler alongside the results + of ``action`` + +Behavioral Customizations ++++++++++++++++++++++++++ + +.. js:function:: openerp.web.ListView.handle_onwrite(record) + + Implements the handling of the ``onwrite`` listview attribute: + calls the RPC methods specified by ``@onwrite``, and if that + method returns an array of ids loads or reloads the records + corresponding to those ids. + + :param record: record being written having triggered the + ``onwrite`` callback + :type record: openerp.web.list.Record + :returns: deferred to all reloadings being done + +Events +++++++ + +For simpler interactions by/with external users of the listview, the +view provides a number of dedicated events to its lifecycle. + +.. note:: if an event is defined as *cancellable*, it means its first + parameter is an object on which the ``cancel`` attribute can + be set. If the ``cancel`` attribute is set, the view will + abort its current behavior as soon as possible, and rollback + any state modification. + + Generally speaking, an event should only be cancelled (by + setting the ``cancel`` flag to ``true``), uncancelling an + event is undefined as event handlers are executed on a + first-come-first-serve basis and later handlers may + re-cancel an uncancelled event. + +.. _listview-edit-before: + +``edit:before`` *cancellable* + + Invoked before the list view starts editing a record. + + Provided with an event object with a single property ``record``, + holding the attributes of the record being edited (``record`` is + empty *but not null* for a new record) + +``edit:after`` + + Invoked after the list view has gone into an edition state, + provided with the attributes of the record being edited (see + ``edit:before``) as first parameter and the form used for the + edition as second parameter. + +``save:before`` *cancellable* + + Invoked right before saving a pending edition, provided with an + event object holding the listview's editor (``editor``) and the + edition form (``form``) + +``save:after`` + + Invoked after a save has been completed + +``cancel:before`` *cancellable* + + Invoked before cancelling a pending edition, provided with the + same information as ``save:before``. + +``cancel:after`` + + Invoked after a pending edition has been cancelled. + +DOM events +++++++++++ + +The list view has grown hooks for the ``keyup`` event on its edition +form (during edition): any such event bubbling out of the edition form +will be forwarded to a method ``keyup_EVENTNAME``, where ``EVENTNAME`` +is the name of the key in ``$.ui.keyCode``. + +The method will also get the event object (originally passed to the +``keyup`` handler) as its sole parameter. + +The base editable list view has handlers for the ``ENTER`` and +``ESCAPE`` keys. + +Editor +------ + +The list-edition modules does not generally interact with the embedded +formview, delegating instead to its +:js:class:`~openerp.web.list.Editor`. + +.. js:class:: openerp.web.list.Editor(parent[, options]) + + The editor object provides a more convenient interface to form + views, and simplifies the usage of form views for semi-arbitrary + edition of stuff. + + However, the editor does *not* task itself with being internally + consistent at this point: calling + e.g. :js:func:`~openerp.web.list.Editor.edit` multiple times in a + row without saving or cancelling each edit is undefined. + + :param parent: + :type parent: :js:class:`~openerp.web.Widget` + :param EditorOptions options: + + .. js:function:: openerp.web.list.Editor.is_editing([record_state]) + + Indicates whether the editor is currently in the process of + providing edition for a record. + + Can be filtered by the state of the record being edited + (whether it's a record being *created* or a record being + *altered*), in which case it asserts both that an edition is + underway and that the record being edited respectively does + not yet exist in the database or already exists there. + + :param record_state: state of the record being edited. + Either ``"new"`` or ``"edit"``. + :type record_state: String + :rtype: Boolean + + .. js:function:: openerp.web.list.Editor.edit(record, configureField[, options]) + + Loads the provided record into the internal form view and + displays the form view. + + Will also attempt to focus the first visible field of the form + view. + + :param Object record: record to load into the form view + (key:value mapping similar to the result + of a ``read``) + :param configureField: function called with each field of the + form view right after the form is + displayed, lets whoever called this + method do some last-minute + configuration of form fields. + :type configureField: Function + :param EditOptions options: + :returns: jQuery delegate to the form object + + .. js:function:: openerp.web.list.Editor.save + + Attempts to save the internal form, then hide it + + :returns: delegate to the record under edition (with ``id`` + added for a creation). The record is not updated + from when it was passed in, aside from the ``id`` + attribute. + + .. js:function:: openerp.web.list.Editor.cancel([force=false]) + + Attemps to cancel the edition of the internal form, then hide + the form + + :param Boolean force: unconditionally cancels the edition of + the internal form, even if the user has + already entered data in it. + :returns: delegate to the record under edition + +.. js:class:: EditorOptions + + .. js:attribute:: EditorOptions.formView + + Form view (sub)-class to instantiate and delegate edition to. + + By default, :js:class:`~openerp.web.FormView` + + .. js:attribute:: EditorOptions.delegate + + Object used to get various bits of information about how to + display stuff. + + By default, uses the editor's parent widget. See + :js:class:`EditorDelegate` for the methods and attributes to + provide. + +.. js:class:: EditorDelegate + + Informal protocol defining the methods and attributes expected of + the :js:class:`~openerp.web.list.Editor`'s delegate. + + .. js:attribute:: EditorDelegate.dataset + + The dataset passed to the form view to synchronize the form + view and the outer widget. + + .. js:function:: EditorDelegate.edition_view(editor) + + Called by the :js:class:`~openerp.web.list.Editor` object to + get a form view (JSON) to pass along to the form view it + created. + + The result should be a valid form view, see :doc:`Form Notes + ` for various peculiarities of the form view + format. + + :param editor: editor object asking for the view + :type editor: :js:class:`~openerp.web.list.Editor` + :returns: form view + :rtype: Object + + .. js:function:: EditorDelegate.prepends_on_create + + By default, the :js:class:`~openerp.web.list.Editor` will + append the ids of newly created records to the + :js:attr:`EditorDelegate.dataset`. If this method returns + ``true``, it will prepend these ids instead. + + :returns: whether new records should be prepended to the + dataset (instead of appended) + :rtype: Boolean + + +.. js:class:: EditOptions + + Options object optionally passed into a method starting an edition + to configure its setup and behavior + + .. js:attribute:: focus_field + + Name of the field to set focus on after setting up the edition + of the record. + + If this option is not provided, or the requested field can not + be focused (invisible, readonly or not in the view), the first + visible non-readonly field is focused. + +Changes from 6.1 +---------------- + +* The editable listview behavior has been rewritten pretty much from + scratch, any code touching on editability will have to be modified + + * The overloading of :js:class:`~openerp.web.ListView.Groups` and + :js:class:`~openerp.web.ListView.List` for editability has been + drastically simplified, and most of the behavior has been moved to + the list view itself. Only + :js:func:`~openerp.web.ListView.List.row_clicked` is still + overridden. + + * A new method ``get_row_for(record) -> jQuery(tr) | null`` has been + added to both ListView.List and ListView.Group, it can be called + from the list view to get the table row matching a record (if such + a row exists). + +* :js:func:`~openerp.web.ListView.do_button_action`'s core behavior + has been split away to + :js:func:`~openerp.web.ListView.handle_button`. This allows bypassing + overrides of :js:func:`~openerp.web.ListView.do_button_action` in a + parent class. + + Ideally, :js:func:`~openerp.web.ListView.handle_button` should not be + overridden. + +* Modifiers handling has been improved (all modifiers information + should now be available through :js:func:`~Column.modifiers_for`, + not just ``invisible``) + +* Changed some handling of the list view's record: a record may now + have no id, and the listview will handle that correctly (for new + records being created) as well as correctly handle the ``id`` being + set. + +* Extended the internal collections structure of the list view with + `#find`_, `#succ`_ and `#pred`_. + +.. _#find: http://underscorejs.org/#find + +.. _#succ: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:succ + +.. _#pred: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:pred diff --git a/addons/web/doc/make.bat b/addons/web/doc/make.bat new file mode 100644 index 0000000..3e72cad --- /dev/null +++ b/addons/web/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\OpenERPWeb.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\OpenERPWeb.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/addons/web/doc/rpc.rst b/addons/web/doc/rpc.rst new file mode 100644 index 0000000..4787978 --- /dev/null +++ b/addons/web/doc/rpc.rst @@ -0,0 +1,276 @@ +Outside the box: network interactions +===================================== + +Building static displays is all nice and good and allows for neat +effects (and sometimes you're given data to display from third parties +so you don't have to make any effort), but a point generally comes +where you'll want to talk to the world and make some network requests. + +OpenERP Web provides two primary APIs to handle this, a low-level +JSON-RPC based API communicating with the Python section of OpenERP +Web (and of your addon, if you have a Python part) and a high-level +API above that allowing your code to talk directly to the OpenERP +server, using familiar-looking calls. + +All networking APIs are :doc:`asynchronous `. As a result, all +of them will return :js:class:`Deferred` objects (whether they resolve +those with values or not). Understanding how those work before before +moving on is probably necessary. + +High-level API: calling into OpenERP models +------------------------------------------- + +Access to OpenERP object methods (made available through XML-RPC from +the server) is done via the :js:class:`openerp.web.Model` class. This +class maps onto the OpenERP server objects via two primary methods, +:js:func:`~openerp.web.Model.call` and +:js:func:`~openerp.web.Model.query`. + +:js:func:`~openerp.web.Model.call` is a direct mapping to the +corresponding method of the OpenERP server object. Its usage is +similar to that of the OpenERP Model API, with three differences: + +* The interface is :doc:`asynchronous `, so instead of + returning results directly RPC method calls will return + :js:class:`Deferred` instances, which will themselves resolve to the + result of the matching RPC call. + +* Because ECMAScript 3/Javascript 1.5 doesnt feature any equivalent to + ``__getattr__`` or ``method_missing``, there needs to be an explicit + method to dispatch RPC methods. + +* No notion of pooler, the model proxy is instantiated where needed, + not fetched from an other (somewhat global) object + +.. code-block:: javascript + + var Users = new Model('res.users'); + + Users.call('change_password', ['oldpassword', 'newpassword'], + {context: some_context}).then(function (result) { + // do something with change_password result + }); + +:js:func:`~openerp.web.Model.query` is a shortcut for a builder-style +interface to searches (``search`` + ``read`` in OpenERP RPC terms). It +returns a :js:class:`~openerp.web.Query` object which is immutable but +allows building new :js:class:`~openerp.web.Query` instances from the +first one, adding new properties or modifiying the parent object's: + +.. code-block:: javascript + + Users.query(['name', 'login', 'user_email', 'signature']) + .filter([['active', '=', true], ['company_id', '=', main_company]]) + .limit(15) + .all().then(function (users) { + // do work with users records + }); + +The query is only actually performed when calling one of the query +serialization methods, :js:func:`~openerp.web.Query.all` and +:js:func:`~openerp.web.Query.first`. These methods will perform a new +RPC call every time they are called. + +For that reason, it's actually possible to keep "intermediate" queries +around and use them differently/add new specifications on them. + +.. js:class:: openerp.web.Model(name) + + .. js:attribute:: openerp.web.Model.name + + name of the OpenERP model this object is bound to + + .. js:function:: openerp.web.Model.call(method[, args][, kwargs]) + + Calls the ``method`` method of the current model, with the + provided positional and keyword arguments. + + :param String method: method to call over rpc on the + :js:attr:`~openerp.web.Model.name` + :param Array<> args: positional arguments to pass to the + method, optional + :param Object<> kwargs: keyword arguments to pass to the + method, optional + :rtype: Deferred<> + + .. js:function:: openerp.web.Model.query(fields) + + :param Array fields: list of fields to fetch during + the search + :returns: a :js:class:`~openerp.web.Query` object + representing the search to perform + +.. js:class:: openerp.web.Query(fields) + + The first set of methods is the "fetching" methods. They perform + RPC queries using the internal data of the object they're called + on. + + .. js:function:: openerp.web.Query.all() + + Fetches the result of the current + :js:class:`~openerp.web.Query` object's search. + + :rtype: Deferred> + + .. js:function:: openerp.web.Query.first() + + Fetches the **first** result of the current + :js:class:`~openerp.web.Query`, or ``null`` if the current + :js:class:`~openerp.web.Query` does have any result. + + :rtype: Deferred + + .. js:function:: openerp.web.Query.count() + + Fetches the number of records the current + :js:class:`~openerp.web.Query` would retrieve. + + :rtype: Deferred + + .. js:function:: openerp.web.Query.group_by(grouping...) + + Fetches the groups for the query, using the first specified + grouping parameter + + :param Array grouping: Lists the levels of grouping + asked of the server. Grouping + can actually be an array or + varargs. + :rtype: Deferred> | null + + The second set of methods is the "mutator" methods, they create a + **new** :js:class:`~openerp.web.Query` object with the relevant + (internal) attribute either augmented or replaced. + + .. js:function:: openerp.web.Query.context(ctx) + + Adds the provided ``ctx`` to the query, on top of any existing + context + + .. js:function:: openerp.web.Query.filter(domain) + + Adds the provided domain to the query, this domain is + ``AND``-ed to the existing query domain. + + .. js:function:: opeenrp.web.Query.offset(offset) + + Sets the provided offset on the query. The new offset + *replaces* the old one. + + .. js:function:: openerp.web.Query.limit(limit) + + Sets the provided limit on the query. The new limit *replaces* + the old one. + + .. js:function:: openerp.web.Query.order_by(fields…) + + Overrides the model's natural order with the provided field + specifications. Behaves much like Django's `QuerySet.order_by + `_: + + * Takes 1..n field names, in order of most to least importance + (the first field is the first sorting key). Fields are + provided as strings. + + * A field specifies an ascending order, unless it is prefixed + with the minus sign "``-``" in which case the field is used + in the descending order + + Divergences from Django's sorting include a lack of random sort + (``?`` field) and the inability to "drill down" into relations + for sorting. + +Aggregation (grouping) +~~~~~~~~~~~~~~~~~~~~~~ + +OpenERP has powerful grouping capacities, but they are kind-of strange +in that they're recursive, and level n+1 relies on data provided +directly by the grouping at level n. As a result, while ``read_group`` +works it's not a very intuitive API. + +OpenERP Web 7.0 eschews direct calls to ``read_group`` in favor of +calling a method of :js:class:`~openerp.web.Query`, `much in the way +it is one in SQLAlchemy +`_ [#]_: + +.. code-block:: javascript + + some_query.group_by(['field1', 'field2']).then(function (groups) { + // do things with the fetched groups + }); + +This method is asynchronous when provided with 1..n fields (to group +on) as argument, but it can also be called without any field (empty +fields collection or nothing at all). In this case, instead of +returning a Deferred object it will return ``null``. + +When grouping criterion come from a third-party and may or may not +list fields (e.g. could be an empty list), this provides two ways to +test the presence of actual subgroups (versus the need to perform a +regular query for records): + +* A check on ``group_by``'s result and two completely separate code + paths + + .. code-block:: javascript + + var groups; + if (groups = some_query.group_by(gby)) { + groups.then(function (gs) { + // groups + }); + } + // no groups + +* Or a more coherent code path using :js:func:`when`'s ability to + coerce values into deferreds: + + .. code-block:: javascript + + $.when(some_query.group_by(gby)).then(function (groups) { + if (!groups) { + // No grouping + } else { + // grouping, even if there are no groups (groups + // itself could be an empty array) + } + }); + +The result of a (successful) :js:func:`~openerp.web.Query.group_by` is +an array of :js:class:`~openerp.web.QueryGroup`. + +Low-level API: RPC calls to Python side +--------------------------------------- + +While the previous section is great for calling core OpenERP code +(models code), it does not work if you want to call the Python side of +OpenERP Web. + +For this, a lower-level API exists on on +:js:class:`~openerp.web.Connection` objects (usually available through +``openerp.connection``): the ``rpc`` method. + +This method simply takes an absolute path (which is the combination of +the Python controller's ``_cp_path`` attribute and the name of the +method you want to call) and a mapping of attributes to values (applied +as keyword arguments on the Python method [#]_). This function fetches +the return value of the Python methods, converted to JSON. + +For instance, to call the ``eval_domain_and_context`` of the +:class:`~web.controllers.main.Session` controller: + +.. code-block:: javascript + + openerp.connection.rpc('/web/session/eval_domain_and_context', { + domains: ds, + contexts: cs + }).then(function (result) { + // handle result + }); + +.. [#] with a small twist: SQLAlchemy's ``orm.query.Query.group_by`` + is not terminal, it returns a query which can still be altered. + +.. [#] except for ``context``, which is extracted and stored in the + request object itself. diff --git a/addons/web/doc/search-view.rst b/addons/web/doc/search-view.rst new file mode 100644 index 0000000..16a91c5 --- /dev/null +++ b/addons/web/doc/search-view.rst @@ -0,0 +1,549 @@ +Search View +=========== + +OpenERP Web 7.0 implements a unified facets-based search view instead +of the previous form-like search view (composed of buttons and +multiple fields). The goal for this change is twofold: + +* Avoid the common issue of users confusing the search view with a + form view and trying to create their records through it (or entering + all their data, hitting the ``Create`` button expecting their record + to be created and losing everything). + +* Improve the looks and behaviors of the view, and the fit within + OpenERP Web's new design. + +The internal structure of the faceted search is inspired by +`VisualSearch `_ +[#previous]_. + +As does VisualSearch, the new search view is based on `Backbone`_ and +makes significant use of Backbone's models and collections (OpenERP +Web's widgets make a good replacement for Backbone's own views). As a +result, understanding the implementation details of the OpenERP Web 7 +search view also requires a basic understanding of Backbone's models, +collections and events. + +.. note:: + + This document may mention *fetching* data. This is a shortcut for + "returning a :js:class:`Deferred` to [whatever is being + fetched]". Unless further noted, the function or method may opt to + return nothing by fetching ``null`` (which can easily be done by + returning ``$.when(null)``, which simply wraps the ``null`` in a + Deferred). + +Working with the search view: creating new inputs +------------------------------------------------- + +The primary component of search views, as with all other OpenERP +views, are inputs. The search view has two types of inputs — filters +and fields — but only one is easly customizable: fields. + +The mapping from OpenERP field types (and widgets) to search view +objects is stored in the ``openerp.web.search.fields`` +:js:class:`~openerp.web.Registry` where new field types and widgets +can be added. + +Search view inputs have four main roles: + +Loading defaults +++++++++++++++++ + +Once the search view has initialized all its inputs, it will call +:js:func:`~openerp.web.search.Input.facet_for_defaults` on each input, +passing it a mapping (a javascript object) of ``name:value`` extracted +from the action's context. + +This method should fetch a :js:class:`~openerp.web.search.Facet` (or +an equivalent object) for the field's default value if applicable (if +a default value for the field is found in the ``defaults`` mapping). + +A default implementation is provided which checks if ``defaults`` +contains a non-falsy value for the field's ``@name`` and calls +:js:func:`openerp.web.search.Input.facet_for` with that value. + +There is no default implementation of +:js:func:`openerp.web.search.Input.facet_for` [#no_impl]_, but +:js:class:`openerp.web.search.Field` provides one, which uses the +value as-is to fetch a :js:class:`~openerp.web.search.Facet`. + +Providing completions ++++++++++++++++++++++ + +An important component of the new search view is the auto-completion +pane, and the task of providing completion items is delegated to +inputs through the :js:func:`~openerp.web.search.Input.complete` +method. + +This method should take a single argument (the string being typed by +the user) and should fetch an ``Array`` of possible completions +[#completion]_. + +A default implementation is provided which fetches nothing. + +A completion item is a javascript object with two keys (technically it +can have any number of keys, but only these two will be used by the +search view): + +``label`` + + The string which will be displayed in the completion pane. It may + be formatted using HTML (inline only), as a result if ``value`` is + interpolated into it it *must* be escaped. ``_.escape`` can be + used for this. + +``facet`` + + Either a :js:class:`~openerp.web.search.Facet` object or (more + commonly) the corresponding attributes object. This is the facet + which will be inserted into the search query if the completion + item is selected by the user. + +If the ``facet`` is not provided (not present, ``null``, ``undefined`` +or any other falsy value), the completion item will not be selectable +and will act as a section title of sort (the ``label`` will be +formatted differently). If an input *may* fetch multiple completion +items, it *should* prefix those with a section title using its own +name. This has no technical consequence but is clearer for users. + +Providing drawer/supplementary UI ++++++++++++++++++++++++++++++++++ + +For some inputs (fields or not), interaction via autocompletion may be +awkward or even impossible. + +These may opt to being rendered in a "drawer" as well or instead. In +that case, they will undergo the normal widget lifecycle and be +rendered inside the drawer. + +.. Found no good type-based way to handle this, since there is no MI + (so no type-tagging) and it's possible for both Field and non-Field + input to be put into the drawer, for whatever reason (e.g. some + sort of auto-detector completion item for date widgets, but a + second more usual calendar widget in the drawer for more + obvious/precise interactions) + +Any input can note its desire to be rendered in the drawer by +returning a truthy value from +:js:func:`~openerp.web.search.Input.in_drawer`. + +By default, :js:func:`~openerp.web.search.Input.in_drawer` returns the +value of :js:attr:`~openerp.web.search.Input._in_drawer`, which is +``false``. The behavior can be toggled either by redefining the +attribute to ``true`` (either on the class or on the input), or by +overriding :js:func:`~openerp.web.search.Input.in_drawer` itself. + +The input will be rendered in the full width of the drawer, it will be +started only once (per view). + +.. todo:: drawer API (if a widget wants to close the drawer in some + way), part of the low-level SearchView API/interactions? + + +.. todo:: handle filters and filter groups via a "driver" input which + dynamically collects, lays out and renders filters? => + exercises drawer thingies + +Converting from facet objects ++++++++++++++++++++++++++++++ + +Ultimately, the point of the search view is to allow searching. In +OpenERP this is done via :ref:`domains `. On +the other hand, the OpenERP Web 7 search view's state is modelled +after a collection of :js:class:`~openerp.web.search.Facet`, and each +field of a search view may have special requirements when it comes to +the domains it produces [#special]_. + +So there needs to be some way of mapping +:js:class:`~openerp.web.search.Facet` objects to OpenERP search data. + +This is done via an input's +:js:func:`~openerp.web.search.Input.get_domain` and +:js:func:`~openerp.web.search.Input.get_context`. Each takes a +:js:class:`~openerp.web.search.Facet` and returns whatever it's +supposed to generate (a domain or a context, respectively). Either can +return ``null`` if the current value does not map to a domain or +context, and can throw an :js:class:`~openerp.web.search.Invalid` +exception if the value is not valid at all for the field. + +.. note:: + + The :js:class:`~openerp.web.search.Facet` object can have any + number of values (from 1 upwards) + +.. note:: + + There is a third conversion method, + :js:func:`~openerp.web.search.Input.get_groupby`, which returns an + ``Array`` of groupby domains rather than a single context. At this + point, it is only implemented on (and used by) filters. + +Programmatic interactions: internal model +----------------------------------------- + +This new searchview is built around an instance of +:js:class:`~openerp.web.search.SearchQuery` available as +:js:attr:`openerp.web.SearchView.query`. + +The query is a `backbone collection`_ of +:js:class:`~openerp.web.search.Facet` objects, which can be interacted +with directly by external objects or search view controls +(e.g. widgets displayed in the drawer). + +.. js:class:: openerp.web.search.SearchQuery + + The current search query of the search view, provides convenience + behaviors for manipulating :js:class:`~openerp.web.search.Facet` + on top of the usual `backbone collection`_ methods. + + The query ensures all of its facets contain at least one + :js:class:`~openerp.web.search.FacetValue` instance. Otherwise, + the facet is automatically removed from the query. + + .. js:function:: openerp.web.search.SearchQuery.add(values, options) + + Overridden from the base ``add`` method so that adding a facet + which is *already* in the collection will merge the value of + the new facet into the old one rather than add a second facet + with different values. + + :param values: facet, facet attributes or array thereof + :returns: the collection itself + + .. js:function:: openerp.web.search.SearchQuery.toggle(value, options) + + Convenience method for toggling facet values in a query: + removes the values (through the facet itself) if they are + present, adds them if they are not. If the facet itself is not + in the collection, adds it automatically. + + A toggling is atomic: only one change event will be triggered + on the facet regardless of the number of values added to or + removed from the facet (if the facet already exists), and the + facet is only removed from the query if it has no value *at + the end* of the toggling. + + :param value: facet or facet attributes + :returns: the collection + +.. js:class:: openerp.web.search.Facet + + A `backbone model`_ representing a single facet of the current + research. May map to a search field, or to a more complex or + fuzzier input (e.g. a custom filter or an advanced search). + + .. js:attribute:: category + + The displayed name of the facet, as a ``String``. This is a + backbone model attribute. + + .. js:attribute:: field + + The :js:class:`~openerp.web.search.Input` instance which + originally created the facet [#facet-field]_, used to delegate + some operations (such as serializing the facet's values to + domains and contexts). This is a backbone model attribute. + + .. js:attribute:: values + + :js:class:`~openerp.web.search.FacetValues` as a javascript + attribute, stores all the values for the facet and helps + propagate their events to the facet. Is also available as a + backbone attribute (via ``#get`` and ``#set``) in which cases + it serializes to and deserializes from javascript arrays (via + ``Collection#toJSON`` and ``Collection#reset``). + + .. js:attribute:: [icon] + + optional, a single ASCII letter (a-z or A-Z) mapping to the + bundled mnmliconsRegular icon font. + + When a facet with an ``icon`` attribute is rendered, the icon + is displayed (in the icon font) in the first section of the + facet instead of the ``category``. + + By default, only filters make use of this facility. + +.. js:class:: openerp.web.search.FacetValues + + `Backbone collection`_ of + :js:class:`~openerp.web.search.FacetValue` instances. + +.. js:class:: openerp.web.search.FacetValue + + `Backbone model`_ representing a single value within a facet, + represents a pair of (displayed name, logical value). + + .. js:attribute:: label + + Backbone model attribute storing the "displayable" + representation of the value, visually output to the + user. Must be a string. + + .. js:attribute:: value + + Backbone model attribute storing the logical/internal value + (of itself), will be used by + :js:class:`~openerp.web.search.Input` to serialize to domains + and contexts. + + Can be of any type. + +Field services +-------------- + +:js:class:`~openerp.web.search.Field` provides a default +implementation of :js:func:`~openerp.web.search.Input.get_domain` and +:js:func:`~openerp.web.search.Input.get_context` taking care of most +of the peculiarities pertaining to OpenERP's handling of fields in +search views. It also provides finer hooks to let developers of new +fields and widgets customize the behavior they want without +necessarily having to reimplement all of +:js:func:`~openerp.web.search.Input.get_domain` or +:js:func:`~openerp.web.search.Input.get_context`: + +.. js:function:: openerp.web.search.Field.get_context(facet) + + If the field has no ``@context``, simply returns + ``null``. Otherwise, calls + :js:func:`~openerp.web.search.Field.value_from` once for each + :js:class:`~openerp.web.search.FacetValue` of the current + :js:class:`~openerp.web.search.Facet` (in order to extract the + basic javascript object from the + :js:class:`~openerp.web.search.FacetValue` then evaluates + ``@context`` with each of these values set as ``self``, and + returns the union of all these contexts. + + :param facet: + :type facet: openerp.web.search.Facet + :returns: a context (literal or compound) + +.. js:function:: openerp.web.search.Field.get_domain(facet) + + If the field has no ``@filter_domain``, calls + :js:func:`~openerp.web.search.Field.make_domain` once with each + :js:class:`~openerp.web.search.FacetValue` of the current + :js:class:`~openerp.web.search.Facet` as well as the field's + ``@name`` and either its ``@operator`` or + :js:attr:`~openerp.web.search.Field.default_operator`. + + If the field has an ``@filter_value``, calls + :js:func:`~openerp.web.search.Field.value_from` once per + :js:class:`~openerp.web.search.FacetValue` and evaluates + ``@filter_value`` with each of these values set as ``self``. + + In either case, "ors" all of the resulting domains (using ``|``) + if there is more than one + :js:class:`~openerp.web.search.FacetValue` and returns the union + of the result. + + :param facet: + :type facet: openerp.web.search.Facet + :returns: a domain (literal or compound) + +.. js:function:: openerp.web.search.Field.make_domain(name, operator, facetValue) + + Builds a literal domain from the provided data. Calls + :js:func:`~openerp.web.search.Field.value_from` on the + :js:class:`~openerp.web.search.FacetValue` and evaluates and sets + it as the domain's third value, uses the other two parameters as + the first two values. + + Can be overridden to build more complex default domains. + + :param String name: the field's name + :param String operator: the operator to use in the field's domain + :param facetValue: + :type facetValue: openerp.web.search.FacetValue + :returns: Array<(String, String, Object)> + +.. js:function:: openerp.web.search.Field.value_from(facetValue) + + Extracts a "bare" javascript value from the provided + :js:class:`~openerp.web.search.FacetValue`, and returns it. + + The default implementation will simply return the ``value`` + backbone property of the argument. + + :param facetValue: + :type facetValue: openerp.web.search.FacetValue + :returns: Object + +.. js:attribute:: openerp.web.search.Field.default_operator + + Operator used to build a domain when a field has no ``@operator`` + or ``@filter_domain``. ``"="`` for + :js:class:`~openerp.web.search.Field` + +Arbitrary data storage +---------------------- + +:js:class:`~openerp.web.search.Facet` and +:js:class:`~openerp.web.search.FacetValue` objects (and structures) +provided by your widgets should never be altered by the search view +(or an other widget). This means you are free to add arbitrary fields +in these structures if you need to (because you have more complex +needs than the attributes described in this document). + +Ideally this should be avoided, but the possibility remains. + +Changes +------- + +.. todo:: merge in changelog instead? + +The displaying of the search view was significantly altered from +OpenERP Web 6.1 to OpenERP Web 7. + +As a result, while the external API used to interact with the search +view does not change many internal details — including the interaction +between the search view and its widgets — were significantly altered: + +Internal operations ++++++++++++++++++++ + +* :js:func:`openerp.web.SearchView.do_clear` has been removed +* :js:func:`openerp.web.SearchView.do_toggle_filter` has been removed + +Widgets API ++++++++++++ + +* :js:func:`openerp.web.search.Widget.render` has been removed + +* :js:func:`openerp.web.search.Widget.make_id` has been removed + +* Search field objects are not openerp widgets anymore, their + ``start`` is not generally called + +* :js:func:`~openerp.web.search.Input.clear` has been removed since + clearing the search view now simply consists of removing all search + facets + +* :js:func:`~openerp.web.search.Input.get_domain` and + :js:func:`~openerp.web.search.Input.get_context` now take a + :js:class:`~openerp.web.search.Facet` as parameter, from which it's + their job to get whatever value they want + +* :js:func:`~openerp.web.search.Input.get_groupby` has been added. It returns + an :js:class:`Array` of context-like constructs. By default, it does not do + anything in :js:class:`~openerp.web.search.Field` and it returns the various + contexts of its enabled filters in + :js:class:`~openerp.web.search.FilterGroup`. + +Filters ++++++++ + +* :js:func:`openerp.web.search.Filter.is_enabled` has been removed + +* :js:class:`~openerp.web.search.FilterGroup` instances are still + rendered (and started) in the "advanced search" drawer. + +Fields +++++++ + +* ``get_value`` has been replaced by + :js:func:`~openerp.web.search.Field.value_from` as it now takes a + :js:class:`~openerp.web.search.FacetValue` argument (instead of no + argument). It provides a default implementation returning the + ``value`` property of its argument. + +* The third argument to + :js:func:`~openerp.web.search.Field.make_domain` is now a + :js:class:`~openerp.web.search.FacetValue` so child classes have all + the information they need to derive the "right" resulting domain. + +Custom filters +++++++++++++++ + +Instead of being an intrinsic part of the search view, custom filters +are now a special case of filter groups. They are treated specially +still, but much less so than they used to be. + +Many To One ++++++++++++ + +* Because the autocompletion service is now provided by the search + view itself, + :js:func:`openerp.web.search.ManyToOneField.setup_autocomplete` has + been removed. + +Advanced Search ++++++++++++++++ + +* The advanced search is now a more standard + :js:class:`~openerp.web.search.Input` configured to be rendered in + the drawer. + +* :js:class:`~openerp.web.search.ExtendedSearchProposition.Field` are + now standard widgets, with the "right" behaviors (they don't rebind + their ``$element`` in ``start()``) + +* The ad-hoc optional setting of the openerp field descriptor on a + :js:class:`~openerp.web.search.ExtendedSearchProposition.Field` has + been removed, the field descriptor is now passed as second argument + to the + :js:class:`~openerp.web.search.ExtendedSearchProposition.Field`'s + constructor, and bound to its + :js:attr:`~openerp.web.search.ExtendedSearchProposition.Field.field`. + +* Instead of its former domain triplet ``(field, operator, value)``, + :js:func:`~openerp.web.search.ExtendedSearchProposition.get_proposition` + now returns an object with two fields ``label`` and ``value``, + respectively a human-readable version of the proposition and the + corresponding domain triplet for the proposition. + +.. [#previous] + + the original view was implemented on top of a monkey-patched + VisualSearch, but as our needs diverged from VisualSearch's goal + this made less and less sense ultimately leading to a clean-room + reimplementation + +.. [#no_impl] + + In case you are extending the search view with a brand new type of + input + +.. [#completion] + + Ideally this array should not hold more than about 10 items, but + the search view does not put any constraint on this at the + moment. Note that this may change. + +.. [#facet-field] + + ``field`` does not actually need to be an instance of + :js:class:`~openerp.web.search.Input`, nor does it need to be what + created the facet, it just needs to provide the three + facet-serialization methods + :js:func:`~openerp.web.search.Input.get_domain`, + :js:func:`~openerp.web.search.Input.get_context` and + :js:func:`~openerp.web.search.Input.get_gropuby`, existing + :js:class:`~openerp.web.search.Input` subtypes merely provide + convenient base implementation for those methods. + + Complex search view inputs (especially those living in the drawer) + may prefer using object literals with the right slots returning + closed-over values or some other scheme un-bound to an actual + :js:class:`~openerp.web.search.Input`, as + :js:class:`~openerp.web.search.CustomFilters` and + :js:class:`~openerp.web.search.Advanced` do. + +.. [#special] + + search view fields may also bundle context data to add to the + search context + +.. _Backbone: + http://documentcloud.github.com/backbone/ + +.. _Backbone.Collection: +.. _Backbone collection: + http://documentcloud.github.com/backbone/#Collection + +.. _Backbone model: + http://documentcloud.github.com/backbone/#Model + +.. _commit 3fca87101d: + https://github.com/documentcloud/visualsearch/commit/3fca87101d diff --git a/addons/web/doc/widget.rst b/addons/web/doc/widget.rst new file mode 100644 index 0000000..ddfe525 --- /dev/null +++ b/addons/web/doc/widget.rst @@ -0,0 +1,274 @@ +User Interaction: Widget +======================== + +This is the base class for all visual components. It corresponds to an MVC +view. It provides a number of services to handle a section of a page: + +* Rendering with QWeb + +* Parenting-child relations + +* Life-cycle management (including facilitating children destruction when a + parent object is removed) + +* DOM insertion, via jQuery-powered insertion methods. Insertion targets can + be anything the corresponding jQuery method accepts (generally selectors, + DOM nodes and jQuery objects): + + :js:func:`~openerp.base.Widget.appendTo` + Renders the widget and inserts it as the last child of the target, uses + `.appendTo()`_ + + :js:func:`~openerp.base.Widget.prependTo` + Renders the widget and inserts it as the first child of the target, uses + `.prependTo()`_ + + :js:func:`~openerp.base.Widget.insertAfter` + Renders the widget and inserts it as the preceding sibling of the target, + uses `.insertAfter()`_ + + :js:func:`~openerp.base.Widget.insertBefore` + Renders the widget and inserts it as the following sibling of the target, + uses `.insertBefore()`_ + +* Backbone-compatible shortcuts + +DOM Root +-------- + +A :js:class:`~openerp.web.Widget` is responsible for a section of the +page materialized by the DOM root of the widget. The DOM root is +available via the :js:attr:`~openerp.web.Widget.el` and +:js:attr:`~openerp.web.Widget.$element` attributes, which are +respectively the raw DOM Element and the jQuery wrapper around the DOM +element. + +There are two main ways to define and generate this DOM root: + +.. js:attribute:: openerp.web.Widget.template + + Should be set to the name of a QWeb template (a + :js:class:`String`). If set, the template will be rendered after + the widget has been initialized but before it has been + started. The root element generated by the template will be set as + the DOM root of the widget. + +.. js:attribute:: openerp.web.Widget.tagName + + Used if the widget has no template defined. Defaults to ``div``, + will be used as the tag name to create the DOM element to set as + the widget's DOM root. It is possible to further customize this + generated DOM root with the following attributes: + + .. js:attribute:: openerp.web.Widget.id + + Used to generate an ``id`` attribute on the generated DOM + root. + + .. js:attribute:: openerp.web.Widget.className + + Used to generate a ``class`` attribute on the generated DOM root. + + .. js:attribute:: openerp.web.Widget.attributes + + Mapping (object literal) of attribute names to attribute + values. Each of these k:v pairs will be set as a DOM attribute + on the generated DOM root. + + None of these is used in case a template is specified on the widget. + +The DOM root can also be defined programmatically by overridding + +.. js:function:: openerp.web.Widget.renderElement + + Renders the widget's DOM root and sets it. The default + implementation will render a set template or generate an element + as described above, and will call + :js:func:`~openerp.web.Widget.setElement` on the result. + + Any override to :js:func:`~openerp.web.Widget.renderElement` which + does not call its ``_super`` **must** call + :js:func:`~openerp.web.Widget.setElement` with whatever it + generated or the widget's behavior is undefined.r + + .. note:: + + The default :js:func:`~openerp.web.Widget.renderElement` can + be called repeatedly, it will *replace* the previous DOM root + (using ``replaceWith``). However, this requires that the + widget correctly sets and unsets its events (and children + widgets). Generally, + :js:func:`~openerp.web.Widget.renderElement` should not be + called repeatedly unless the widget advertizes this feature. + +Accessing DOM content +~~~~~~~~~~~~~~~~~~~~~ + +Because a widget is only responsible for the content below its DOM +root, there is a shortcut for selecting sub-sections of a widget's +DOM: + +.. js:function:: openerp.web.Widget.$(selector) + + Applies the CSS selector specified as parameter to the widget's + DOM root. + + .. code-block:: javascript + + this.$(selector); + + is functionally identical to: + + .. code-block:: javascript + + this.$element.find(selector); + + :param String selector: CSS selector + :returns: jQuery object + + .. note:: this helper method is compatible with + ``Backbone.View.$`` + +Resetting the DOM root +~~~~~~~~~~~~~~~~~~~~~~ + +.. js:function:: openerp.web.Widget.setElement(element) + + Re-sets the widget's DOM root to the provided element, also + handles re-setting the various aliases of the DOM root as well as + unsetting and re-setting delegated events. + + :param Element element: a DOM element or jQuery object to set as + the widget's DOM root + + .. note:: should be mostly compatible with `Backbone's + setElement`_ + +DOM events handling +------------------- + +A widget will generally need to respond to user action within its +section of the page. This entails binding events to DOM elements. + +To this end, :js:class:`~openerp.web.Widget` provides an shortcut: + +.. js:attribute:: openerp.web.Widget.events + + Events are a mapping of ``event selector`` (an event name and a + CSS selector separated by a space) to a callback. The callback can + be either a method name in the widget or a function. In either + case, the ``this`` will be set to the widget. + + The selector is used for jQuery's `event delegation`_, the + callback will only be triggered for descendants of the DOM root + matching the selector [0]_. If the selector is left out (only an + event name is specified), the event will be set directly on the + widget's DOM root. + +.. js:function:: openerp.web.Widget.delegateEvents + + This method is in charge of binding + :js:attr:`~openerp.web.Widget.events` to the DOM. It is + automatically called after setting the widget's DOM root. + + It can be overridden to set up more complex events than the + :js:attr:`~openerp.web.Widget.events` map allows, but the parent + should always be called (or :js:attr:`~openerp.web.Widget.events` + won't be handled correctly). + +.. js:function:: openerp.web.Widget.undelegateEvents + + This method is in charge of unbinding + :js:attr:`~openerp.web.Widget.events` from the DOM root when the + widget is destroyed or the DOM root is reset, in order to avoid + leaving "phantom" events. + + It should be overridden to un-set any event set in an override of + :js:func:`~openerp.web.Widget.delegateEvents`. + +.. note:: this behavior should be compatible with `Backbone's + delegateEvents`_, apart from not accepting any argument. + +Subclassing Widget +------------------ + +:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the +:js:func:`~openerp.base.Class.extend` method), and provides a number of +abstract properties and concrete methods (which you may or may not want to +override). Creating a subclass looks like this: + +.. code-block:: javascript + + var MyWidget = openerp.base.Widget.extend({ + // QWeb template to use when rendering the object + template: "MyQWebTemplate", + + init: function(parent) { + this._super(parent); + // insert code to execute before rendering, for object + // initialization + }, + start: function() { + this._super(); + // post-rendering initialization code, at this point + // ``this.$element`` has been initialized + this.$element.find(".my_button").click(/* an example of event binding * /); + + // if ``start`` is asynchronous, return a promise object so callers + // know when the object is done initializing + return this.rpc(/* … */) + } + }); + +The new class can then be used in the following manner: + +.. code-block:: javascript + + // Create the instance + var my_widget = new MyWidget(this); + // Render and insert into DOM + my_widget.appendTo(".some-div"); + +After these two lines have executed (and any promise returned by ``appendTo`` +has been resolved if needed), the widget is ready to be used. + +.. note:: the insertion methods will start the widget themselves, and will + return the result of :js:func:`~openerp.base.Widget.start()`. + + If for some reason you do not want to call these methods, you will + have to first call :js:func:`~openerp.base.Widget.render()` on the + widget, then insert it into your DOM and start it. + +If the widget is not needed anymore (because it's transient), simply terminate +it: + +.. code-block:: javascript + + my_widget.destroy(); + +will unbind all DOM events, remove the widget's content from the DOM and +destroy all widget data. + +.. [0] not all DOM events are compatible with events delegation + +.. _.appendTo(): + http://api.jquery.com/appendTo/ + +.. _.prependTo(): + http://api.jquery.com/prependTo/ + +.. _.insertAfter(): + http://api.jquery.com/insertAfter/ + +.. _.insertBefore(): + http://api.jquery.com/insertBefore/ + +.. _event delegation: + http://api.jquery.com/delegate/ + +.. _Backbone's setElement: + http://backbonejs.org/#View-setElement + +.. _Backbone's delegateEvents: + http://backbonejs.org/#View-delegateEvents + diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index c1eff18..0000000 --- a/doc/Makefile +++ /dev/null @@ -1,154 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -q -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - sed -i '/-99999/d' _build/dirhtml/_static/flasky.css - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/_static/openerp.png b/doc/_static/openerp.png deleted file mode 100644 index d6dbd9d..0000000 Binary files a/doc/_static/openerp.png and /dev/null differ diff --git a/doc/_templates/sidebarintro.html b/doc/_templates/sidebarintro.html deleted file mode 100644 index ecd5198..0000000 --- a/doc/_templates/sidebarintro.html +++ /dev/null @@ -1,16 +0,0 @@ - - -

Other Docs

- - -

Useful Links

- diff --git a/doc/_templates/sidebarlogo.html b/doc/_templates/sidebarlogo.html deleted file mode 100644 index de6e3e5..0000000 --- a/doc/_templates/sidebarlogo.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/doc/_themes/LICENSE b/doc/_themes/LICENSE deleted file mode 100644 index 8daab7e..0000000 --- a/doc/_themes/LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -Copyright (c) 2010 by Armin Ronacher. - -Some rights reserved. - -Redistribution and use in source and binary forms of the theme, 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. - -* The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. - -We kindly ask you to only use these themes in an unmodified manner just -for Flask and Flask-related products, not for unrelated projects. If you -like the visual style and want to use it for your own projects, please -consider making some larger changes to the themes (such as changing -font faces, sizes, colors or margins). - -THIS THEME 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 THEME, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/doc/_themes/README b/doc/_themes/README deleted file mode 100644 index b3292bd..0000000 --- a/doc/_themes/README +++ /dev/null @@ -1,31 +0,0 @@ -Flask Sphinx Styles -=================== - -This repository contains sphinx styles for Flask and Flask related -projects. To use this style in your Sphinx documentation, follow -this guide: - -1. put this folder as _themes into your docs folder. Alternatively - you can also use git submodules to check out the contents there. -2. add this to your conf.py: - - sys.path.append(os.path.abspath('_themes')) - html_theme_path = ['_themes'] - html_theme = 'flask' - -The following themes exist: - -- 'flask' - the standard flask documentation theme for large - projects -- 'flask_small' - small one-page theme. Intended to be used by - very small addon libraries for flask. - -The following options exist for the flask_small theme: - - [options] - index_logo = '' filename of a picture in _static - to be used as replacement for the - h1 in the index.rst file. - index_logo_height = 120px height of the index logo - github_fork = '' repository name on github for the - "fork me" badge diff --git a/doc/_themes/flask/layout.html b/doc/_themes/flask/layout.html deleted file mode 100644 index ad08ecc..0000000 --- a/doc/_themes/flask/layout.html +++ /dev/null @@ -1,25 +0,0 @@ -{%- extends "basic/layout.html" %} -{%- block extrahead %} - {{ super() }} - {% if theme_touch_icon %} - - {% endif %} - -{% endblock %} -{%- block relbar2 %}{% endblock %} -{% block header %} - {{ super() }} - {% if pagename == 'index' %} -
- {% endif %} -{% endblock %} -{%- block footer %} - - {% if pagename == 'index' %} -
- {% endif %} -{%- endblock %} diff --git a/doc/_themes/flask/relations.html b/doc/_themes/flask/relations.html deleted file mode 100644 index 3bbcde8..0000000 --- a/doc/_themes/flask/relations.html +++ /dev/null @@ -1,19 +0,0 @@ -

Related Topics

- diff --git a/doc/_themes/flask/static/flasky.css_t b/doc/_themes/flask/static/flasky.css_t deleted file mode 100644 index 0280e15..0000000 --- a/doc/_themes/flask/static/flasky.css_t +++ /dev/null @@ -1,395 +0,0 @@ -/* - * flasky.css_t - * ~~~~~~~~~~~~ - * - * :copyright: Copyright 2010 by Armin Ronacher. - * :license: Flask Design License, see LICENSE for details. - */ - -{% set page_width = '80em' %} -{% set sidebar_width = '16em' %} - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Georgia', serif; - font-size: 15px; - background-color: white; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - width: {{ page_width }}; - margin: 30px auto 0 auto; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 {{ sidebar_width }}; -} - -div.sphinxsidebar { - width: {{ sidebar_width }}; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 0px 0 0px; -} - -img.floatingflask { - padding: 0 0 10px 10px; - float: right; -} - -div.footer { - width: {{ page_width }}; - margin: 20px auto 30px auto; - font-size: 12px; - color: #888; - text-align: right; -} - -div.footer a { - color: #888; -} - -div.related { - display: none; -} - -div.sphinxsidebar a { - color: #444; - text-decoration: none; - border-bottom: 1px dotted #999; -} - -div.sphinxsidebar a:hover { - border-bottom: 1px solid #999; -} - -div.sphinxsidebar { - font-size: 12px; - line-height: 1.5; -} - -div.sphinxsidebarwrapper { - padding: 0px 10px; -} - -div.sphinxsidebarwrapper p.logo { - padding: 0 0 20px 0; - margin: 0; - text-align: center; -} - -div.sphinxsidebar h3, -div.sphinxsidebar h4 { - font-family: 'Garamond', 'Georgia', serif; - color: #444; - font-size: 22px; - font-weight: normal; - margin: 0 0 5px 0; - padding: 0; -} - -div.sphinxsidebar h4 { - font-size: 18px; -} - -div.sphinxsidebar h3 a { - color: #444; -} - -div.sphinxsidebar p.logo a, -div.sphinxsidebar h3 a, -div.sphinxsidebar p.logo a:hover, -div.sphinxsidebar h3 a:hover { - border: none; -} - -div.sphinxsidebar p { - color: #555; - margin: 10px 0; -} - -div.sphinxsidebar ul { - margin: 10px 0; - padding: 0; - color: #000; -} - -div.sphinxsidebar input { - border: 1px solid #ccc; - font-family: 'Georgia', serif; - font-size: 1em; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -{% if theme_index_logo %} -div.indexwrapper h1 { - text-indent: -999999px; - background: url({{ theme_index_logo }}) no-repeat center center; - height: {{ theme_index_logo_height }}; -} -{% endif %} - -div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #ddd; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #eaeaea; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - background: #fafafa; - margin: 20px -30px; - padding: 10px 30px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -div.admonition tt.xref, div.admonition a tt { - border-bottom: 1px solid #fafafa; -} - -dd div.admonition { - margin-left: -60px; - padding-left: 60px; -} - -div.admonition p.admonition-title { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - font-size: 22px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight { - background-color: white; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt { - font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.9em; -} - -img.screenshot { -} - -tt.descname, tt.descclassname { - font-size: 0.95em; -} - -tt.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #eee; - background: #fdfdfd; - font-size: 0.9em; -} - -table.footnote + table.footnote { - margin-top: -15px; - border-top: none; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.footnote td.label { - width: 0px; - padding: 0.3em 0 0.3em 0.5em; -} - -table.footnote td { - padding: 0.3em 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -blockquote { - margin: 0 0 0 30px; - padding: 0; -} - -ul, ol { - margin: 10px 0 10px 30px; - padding: 0; -} - -pre { - background: #eee; - padding: 7px 30px; - margin: 15px -30px; - line-height: 1.3em; -} - -dl pre, blockquote pre, li pre { - margin-left: -60px; - padding-left: 60px; -} - -dl dl pre { - margin-left: -90px; - padding-left: 90px; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, a tt { - background-color: #FBFBFB; - border-bottom: 1px solid white; -} - -a.reference { - text-decoration: none; - border-bottom: 1px dotted #004B6B; -} - -a.reference:hover { - border-bottom: 1px solid #6D4100; -} - -a.footnote-reference { - text-decoration: none; - font-size: 0.7em; - vertical-align: top; - border-bottom: 1px dotted #004B6B; -} - -a.footnote-reference:hover { - border-bottom: 1px solid #6D4100; -} - -a:hover tt { - background: #EEE; -} diff --git a/doc/_themes/flask/static/small_flask.css b/doc/_themes/flask/static/small_flask.css deleted file mode 100644 index 1c6df30..0000000 --- a/doc/_themes/flask/static/small_flask.css +++ /dev/null @@ -1,70 +0,0 @@ -/* - * small_flask.css_t - * ~~~~~~~~~~~~~~~~~ - * - * :copyright: Copyright 2010 by Armin Ronacher. - * :license: Flask Design License, see LICENSE for details. - */ - -body { - margin: 0; - padding: 20px 30px; -} - -div.documentwrapper { - float: none; - background: white; -} - -div.sphinxsidebar { - display: block; - float: none; - width: 102.5%; - margin: 50px -30px -20px -30px; - padding: 10px 20px; - background: #333; - color: white; -} - -div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, -div.sphinxsidebar h3 a { - color: white; -} - -div.sphinxsidebar a { - color: #aaa; -} - -div.sphinxsidebar p.logo { - display: none; -} - -div.document { - width: 100%; - margin: 0; -} - -div.related { - display: block; - margin: 0; - padding: 10px 0 20px 0; -} - -div.related ul, -div.related ul li { - margin: 0; - padding: 0; -} - -div.footer { - display: none; -} - -div.bodywrapper { - margin: 0; -} - -div.body { - min-height: 0; - padding: 0; -} diff --git a/doc/_themes/flask/theme.conf b/doc/_themes/flask/theme.conf deleted file mode 100644 index 18c720f..0000000 --- a/doc/_themes/flask/theme.conf +++ /dev/null @@ -1,9 +0,0 @@ -[theme] -inherit = basic -stylesheet = flasky.css -pygments_style = flask_theme_support.FlaskyStyle - -[options] -index_logo = '' -index_logo_height = 120px -touch_icon = diff --git a/doc/_themes/flask_small/layout.html b/doc/_themes/flask_small/layout.html deleted file mode 100644 index aa1716a..0000000 --- a/doc/_themes/flask_small/layout.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "basic/layout.html" %} -{% block header %} - {{ super() }} - {% if pagename == 'index' %} -
- {% endif %} -{% endblock %} -{% block footer %} - {% if pagename == 'index' %} -
- {% endif %} -{% endblock %} -{# do not display relbars #} -{% block relbar1 %}{% endblock %} -{% block relbar2 %} - {% if theme_github_fork %} - Fork me on GitHub - {% endif %} -{% endblock %} -{% block sidebar1 %}{% endblock %} -{% block sidebar2 %}{% endblock %} diff --git a/doc/_themes/flask_small/static/flasky.css_t b/doc/_themes/flask_small/static/flasky.css_t deleted file mode 100644 index fe2141c..0000000 --- a/doc/_themes/flask_small/static/flasky.css_t +++ /dev/null @@ -1,287 +0,0 @@ -/* - * flasky.css_t - * ~~~~~~~~~~~~ - * - * Sphinx stylesheet -- flasky theme based on nature theme. - * - * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Georgia', serif; - font-size: 17px; - color: #000; - background: white; - margin: 0; - padding: 0; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 40px auto 0 auto; - width: 700px; -} - -hr { - border: 1px solid #B1B4B6; -} - -div.body { - background-color: #ffffff; - color: #3E4349; - padding: 0 30px 30px 30px; -} - -img.floatingflask { - padding: 0 0 10px 10px; - float: right; -} - -div.footer { - text-align: right; - color: #888; - padding: 10px; - font-size: 14px; - width: 650px; - margin: 0 auto 40px auto; -} - -div.footer a { - color: #888; - text-decoration: underline; -} - -div.related { - line-height: 32px; - color: #888; -} - -div.related ul { - padding: 0 0 0 10px; -} - -div.related a { - color: #444; -} - -/* -- body styles ----------------------------------------------------------- */ - -a { - color: #004B6B; - text-decoration: underline; -} - -a:hover { - color: #6D4100; - text-decoration: underline; -} - -div.body { - padding-bottom: 40px; /* saved for footer */ -} - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - margin: 30px 0px 10px 0px; - padding: 0; -} - -{% if theme_index_logo %} -div.indexwrapper h1 { - text-indent: -999999px; - background: url({{ theme_index_logo }}) no-repeat center center; - height: {{ theme_index_logo_height }}; -} -{% endif %} - -div.body h2 { font-size: 180%; } -div.body h3 { font-size: 150%; } -div.body h4 { font-size: 130%; } -div.body h5 { font-size: 100%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: white; - padding: 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - color: #444; - background: #eaeaea; -} - -div.body p, div.body dd, div.body li { - line-height: 1.4em; -} - -div.admonition { - background: #fafafa; - margin: 20px -30px; - padding: 10px 30px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #ccc; -} - -div.admonition p.admonition-title { - font-family: 'Garamond', 'Georgia', serif; - font-weight: normal; - font-size: 24px; - margin: 0 0 10px 0; - padding: 0; - line-height: 1; -} - -div.admonition p.last { - margin-bottom: 0; -} - -div.highlight{ - background-color: white; -} - -dt:target, .highlight { - background: #FAF3E8; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre, tt { - font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.85em; -} - -img.screenshot { -} - -tt.descname, tt.descclassname { - font-size: 0.95em; -} - -tt.descname { - padding-right: 0.08em; -} - -img.screenshot { - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils { - border: 1px solid #888; - -moz-box-shadow: 2px 2px 4px #eee; - -webkit-box-shadow: 2px 2px 4px #eee; - box-shadow: 2px 2px 4px #eee; -} - -table.docutils td, table.docutils th { - border: 1px solid #888; - padding: 0.25em 0.7em; -} - -table.field-list, table.footnote { - border: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -table.footnote { - margin: 15px 0; - width: 100%; - border: 1px solid #eee; -} - -table.field-list th { - padding: 0 0.8em 0 0; -} - -table.field-list td { - padding: 0; -} - -table.footnote td { - padding: 0.5em; -} - -dl { - margin: 0; - padding: 0; -} - -dl dd { - margin-left: 30px; -} - -pre { - padding: 0; - margin: 15px -30px; - padding: 8px; - line-height: 1.3em; - padding: 7px 30px; - background: #eee; - border-radius: 2px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; -} - -dl pre { - margin-left: -60px; - padding-left: 60px; -} - -tt { - background-color: #ecf0f3; - color: #222; - /* padding: 1px 2px; */ -} - -tt.xref, a tt { - background-color: #FBFBFB; -} - -a:hover tt { - background: #EEE; -} diff --git a/doc/_themes/flask_small/theme.conf b/doc/_themes/flask_small/theme.conf deleted file mode 100644 index 542b462..0000000 --- a/doc/_themes/flask_small/theme.conf +++ /dev/null @@ -1,10 +0,0 @@ -[theme] -inherit = basic -stylesheet = flasky.css -nosidebar = true -pygments_style = flask_theme_support.FlaskyStyle - -[options] -index_logo = '' -index_logo_height = 120px -github_fork = '' diff --git a/doc/_themes/flask_theme_support.py b/doc/_themes/flask_theme_support.py deleted file mode 100644 index 33f4744..0000000 --- a/doc/_themes/flask_theme_support.py +++ /dev/null @@ -1,86 +0,0 @@ -# flasky extensions. flasky pygments style based on tango style -from pygments.style import Style -from pygments.token import Keyword, Name, Comment, String, Error, \ - Number, Operator, Generic, Whitespace, Punctuation, Other, Literal - - -class FlaskyStyle(Style): - background_color = "#f8f8f8" - default_style = "" - - styles = { - # No corresponding class for the following: - #Text: "", # class: '' - Whitespace: "underline #f8f8f8", # class: 'w' - Error: "#a40000 border:#ef2929", # class: 'err' - Other: "#000000", # class 'x' - - Comment: "italic #8f5902", # class: 'c' - Comment.Preproc: "noitalic", # class: 'cp' - - Keyword: "bold #004461", # class: 'k' - Keyword.Constant: "bold #004461", # class: 'kc' - Keyword.Declaration: "bold #004461", # class: 'kd' - Keyword.Namespace: "bold #004461", # class: 'kn' - Keyword.Pseudo: "bold #004461", # class: 'kp' - Keyword.Reserved: "bold #004461", # class: 'kr' - Keyword.Type: "bold #004461", # class: 'kt' - - Operator: "#582800", # class: 'o' - Operator.Word: "bold #004461", # class: 'ow' - like keywords - - Punctuation: "bold #000000", # class: 'p' - - # because special names such as Name.Class, Name.Function, etc. - # are not recognized as such later in the parsing, we choose them - # to look the same as ordinary variables. - Name: "#000000", # class: 'n' - Name.Attribute: "#c4a000", # class: 'na' - to be revised - Name.Builtin: "#004461", # class: 'nb' - Name.Builtin.Pseudo: "#3465a4", # class: 'bp' - Name.Class: "#000000", # class: 'nc' - to be revised - Name.Constant: "#000000", # class: 'no' - to be revised - Name.Decorator: "#888", # class: 'nd' - to be revised - Name.Entity: "#ce5c00", # class: 'ni' - Name.Exception: "bold #cc0000", # class: 'ne' - Name.Function: "#000000", # class: 'nf' - Name.Property: "#000000", # class: 'py' - Name.Label: "#f57900", # class: 'nl' - Name.Namespace: "#000000", # class: 'nn' - to be revised - Name.Other: "#000000", # class: 'nx' - Name.Tag: "bold #004461", # class: 'nt' - like a keyword - Name.Variable: "#000000", # class: 'nv' - to be revised - Name.Variable.Class: "#000000", # class: 'vc' - to be revised - Name.Variable.Global: "#000000", # class: 'vg' - to be revised - Name.Variable.Instance: "#000000", # class: 'vi' - to be revised - - Number: "#990000", # class: 'm' - - Literal: "#000000", # class: 'l' - Literal.Date: "#000000", # class: 'ld' - - String: "#4e9a06", # class: 's' - String.Backtick: "#4e9a06", # class: 'sb' - String.Char: "#4e9a06", # class: 'sc' - String.Doc: "italic #8f5902", # class: 'sd' - like a comment - String.Double: "#4e9a06", # class: 's2' - String.Escape: "#4e9a06", # class: 'se' - String.Heredoc: "#4e9a06", # class: 'sh' - String.Interpol: "#4e9a06", # class: 'si' - String.Other: "#4e9a06", # class: 'sx' - String.Regex: "#4e9a06", # class: 'sr' - String.Single: "#4e9a06", # class: 's1' - String.Symbol: "#4e9a06", # class: 'ss' - - Generic: "#000000", # class: 'g' - Generic.Deleted: "#a40000", # class: 'gd' - Generic.Emph: "italic #000000", # class: 'ge' - Generic.Error: "#ef2929", # class: 'gr' - Generic.Heading: "bold #000080", # class: 'gh' - Generic.Inserted: "#00A000", # class: 'gi' - Generic.Output: "#888", # class: 'go' - Generic.Prompt: "#745334", # class: 'gp' - Generic.Strong: "bold #000000", # class: 'gs' - Generic.Subheading: "bold #800080", # class: 'gu' - Generic.Traceback: "bold #a40000", # class: 'gt' - } diff --git a/doc/addon-structure.txt b/doc/addon-structure.txt deleted file mode 100644 index 3815174..0000000 --- a/doc/addon-structure.txt +++ /dev/null @@ -1,12 +0,0 @@ - - +-- __openerp__.py - +-- controllers/ - +-- static/ - +-- lib/ - +-- src/ - +-- css/ - +-- img/ - +-- js/ - +-- xml/ - +-- test/ - +-- test/ diff --git a/doc/async.rst b/doc/async.rst deleted file mode 100644 index 23b3409..0000000 --- a/doc/async.rst +++ /dev/null @@ -1,353 +0,0 @@ -Don't stop the world now: asynchronous development and Javascript -================================================================= - -As a language (and runtime), javascript is fundamentally -single-threaded. This means any blocking request or computation will -blocks the whole page (and, in older browsers, the software itself -even preventing users from switching to an other tab): a javascript -environment can be seen as an event-based runloop where application -developers have no control over the runloop itself. - -As a result, performing long-running synchronous network requests or -other types of complex and expensive accesses is frowned upon and -asynchronous APIs are used instead. - -Asynchronous code rarely comes naturally, especially for developers -used to synchronous server-side code (in Python, Java or C#) where the -code will just block until the deed is gone. This is increased further -when asynchronous programming is not a first-class concept and is -instead implemented on top of callbacks-based programming, which is -the case in javascript. - -The goal of this guide is to provide some tools to deal with -asynchronous systems, and warn against systematic issues or dangers. - -Deferreds ---------- - -Deferreds are a form of `promises`_. OpenERP Web currently uses -`jQuery's deferred`_, but any `CommonJS Promises/A`_ implementation -should work. - -The core idea of deferreds is that potentially asynchronous methods -will return a :js:class:`Deferred` object instead of an arbitrary -value or (most commonly) nothing. - -This object can then be used to track the end of the asynchronous -operation by adding callbacks onto it, either success callbacks or -error callbacks. - -A great advantage of deferreds over simply passing callback functions -directly to asynchronous methods is the ability to :ref:`compose them -`. - -Using deferreds -~~~~~~~~~~~~~~~ - -`CommonJS Promises/A`_ deferreds have only one method of importance: -:js:func:`Deferred.then`. This method is used to attach new callbacks -to the deferred object. - -* the first parameter attaches a success callback, called when the - deferred object is successfully resolved and provided with the - resolved value(s) for the asynchronous operation. - -* the second parameter attaches a failure callback, called when the - deferred object is rejected and provided with rejection values - (often some sort of error message). - -Callbacks attached to deferreds are never "lost": if a callback is -attached to an already resolved or rejected deferred, the callback -will be called (or ignored) immediately. A deferred is also only ever -resolved or rejected once, and is either resolved or rejected: a given -deferred can not call a single success callback twice, or call both a -success and a failure callbacks. - -:js:func:`~Deferred.then` should be the method you'll use most often -when interacting with deferred objects (and thus asynchronous APIs). - -Building deferreds -~~~~~~~~~~~~~~~~~~ - -After using asynchronous APIs may come the time to build them: for -`mocks`_, to compose deferreds from multiple source in a complex -manner, in order to let the current operations repaint the screen or -give other events the time to unfold, ... - -This is easy using jQuery's deferred objects. - -.. note:: this section is an implementation detail of jQuery Deferred - objects, the creation of promises is not part of any - standard (even tentative) that I know of. If you are using - deferred objects which are not jQuery's, their API may (and - often will) be completely different. - -Deferreds are created by invoking their constructor [#]_ without any -argument. This creates a :js:class:`Deferred` instance object with the -following methods: - -:js:func:`Deferred.resolve` - - As its name indicates, this method moves the deferred to the - "Resolved" state. It can be provided as many arguments as - necessary, these arguments will be provided to any pending success - callback. - -:js:func:`Deferred.reject` - - Similar to :js:func:`~Deferred.resolve`, but moves the deferred to - the "Rejected" state and calls pending failure handlers. - -:js:func:`Deferred.promise` - - Creates a readonly view of the deferred object. It is generally a - good idea to return a promise view of the deferred to prevent - callers from resolving or rejecting the deferred in your stead. - -:js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used -to inform callers that the asynchronous operation has failed (or -succeeded). These methods should simply be called when the -asynchronous operation has ended, to notify anybody interested in its -result(s). - -.. _deferred-composition: - -Composing deferreds -~~~~~~~~~~~~~~~~~~~ - -What we've seen so far is pretty nice, but mostly doable by passing -functions to other functions (well adding functions post-facto would -probably be a chore... still, doable). - -Deferreds truly shine when code needs to compose asynchronous -operations in some way or other, as they can be used as a basis for -such composition. - -There are two main forms of compositions over deferred: multiplexing -and piping/cascading. - -Deferred multiplexing -````````````````````` - -The most common reason for multiplexing deferred is simply performing -2+ asynchronous operations and wanting to wait until all of them are -done before moving on (and executing more stuff). - -The jQuery multiplexing function for promises is :js:func:`when`. - -.. note:: the multiplexing behavior of jQuery's :js:func:`when` is an - (incompatible, mostly) extension of the behavior defined in - `CommonJS Promises/B`_. - -This function can take any number of promises [#]_ and will return a -promise. - -This returned promise will be resolved when *all* multiplexed promises -are resolved, and will be rejected as soon as one of the multiplexed -promises is rejected (it behaves like Python's ``all()``, but with -promise objects instead of boolean-ish). - -The resolved values of the various promises multiplexed via -:js:func:`when` are mapped to the arguments of :js:func:`when`'s -success callback, if they are needed. The resolved values of a promise -are at the same index in the callback's arguments as the promise in -the :js:func:`when` call so you will have: - -.. code-block:: javascript - - $.when(p0, p1, p2, p3).then( - function (results0, results1, results2, results3) { - // code - }); - -.. warning:: - - in a normal mapping, each parameter to the callback would be an - array: each promise is conceptually resolved with an array of 0..n - values and these values are passed to :js:func:`when`'s - callback. But jQuery treats deferreds resolving a single value - specially, and "unwraps" that value. - - For instance, in the code block above if the index of each promise - is the number of values it resolves (0 to 3), ``results0`` is an - empty array, ``results2`` is an array of 2 elements (a pair) but - ``results1`` is the actual value resolved by ``p1``, not an array. - -Deferred chaining -````````````````` - -A second useful composition is starting an asynchronous operation as -the result of an other asynchronous operation, and wanting the result -of both: :js:func:`Deferred.then` returns the deferred on which it was -called, so handle e.g. OpenERP's search/read sequence with this would -require something along the lines of: - -.. code-block:: javascript - - var result = $.Deferred(); - Model.search(condition).then(function (ids) { - Model.read(ids, fields).then(function (records) { - result.resolve(records); - }); - }); - return result.promise(); - -While it doesn't look too bad for trivial code, this quickly gets -unwieldy. - -Instead, jQuery provides a tool to handle this kind of chains: -:js:func:`Deferred.pipe`. - -:js:func:`~Deferred.pipe` has the same signature as -:js:func:`~Deferred.then` and could be used in the same manner -provided its return value was not used. - -It differs from :js:func:`~Deferred.then` in two ways: it returns a -new promise object, not the one it was called with, and the return -values of the callbacks is actually important to it: whichever -callback is called, - -* If the callback is not set (not provided or left to null), the - resolution or rejection value(s) is simply forwarded to - :js:func:`~Deferred.pipe`'s promise (it's essentially a noop) - -* If the callback is set and does not return an observable object (a - deferred or a promise), the value it returns (``undefined`` if it - does not return anything) will replace the value it was given, e.g. - - .. code-block:: javascript - - promise.pipe(function () { - console.log('called'); - }); - - will resolve with the sole value ``undefined``. - -* If the callback is set and returns an observable object, that object - will be the actual resolution (and result) of the pipe. This means a - resolved promise from the failure callback will resolve the pipe, - and a failure promise from the success callback will reject the - pipe. - - This provides an easy way to chain operation successes, and the - previous piece of code can now be rewritten: - - .. code-block:: javascript - - return Model.search(condition).pipe(function (ids) { - return Model.read(ids, fields); - }); - - the result of the whole expression will encode failure if either - ``search`` or ``read`` fails (with the right rejection values), and - will be resolved with ``read``'s resolution values if the chain - executes correctly. - -:js:func:`~Deferred.pipe` is also useful to adapt third-party -promise-based APIs, in order to filter their resolution value counts -for instance (to take advantage of :js:func:`when` 's special treatment -of single-value promises). - -jQuery.Deferred API -~~~~~~~~~~~~~~~~~~~ - -.. js:function:: when(deferreds…) - - :param deferreds: deferred objects to multiplex - :returns: a multiplexed deferred - :rtype: :js:class:`Deferred` - -.. js:class:: Deferred - - .. js:function:: Deferred.then(doneCallback[, failCallback]) - - Attaches new callbacks to the resolution or rejection of the - deferred object. Callbacks are executed in the order they are - attached to the deferred. - - To provide only a failure callback, pass ``null`` as the - ``doneCallback``, to provide only a success callback the - second argument can just be ignored (and not passed at all). - - :param doneCallback: function called when the deferred is resolved - :type doneCallback: Function - :param failCallback: function called when the deferred is rejected - :type failCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.done(doneCallback) - - Attaches a new success callback to the deferred, shortcut for - ``deferred.then(doneCallback)``. - - This is a jQuery extension to `CommonJS Promises/A`_ providing - little value over calling :js:func:`~Deferred.then` directly, - it should be avoided. - - :param doneCallback: function called when the deferred is resolved - :type doneCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.fail(failCallback) - - Attaches a new failure callback to the deferred, shortcut for - ``deferred.then(null, failCallback)``. - - A second jQuery extension to `Promises/A `_. Although it provides more value than - :js:func:`~Deferred.done`, it still is not much and should be - avoided as well. - - :param failCallback: function called when the deferred is rejected - :type failCallback: Function - :returns: the deferred object on which it was called - :rtype: :js:class:`Deferred` - - .. js:function:: Deferred.promise() - - Returns a read-only view of the deferred object, with all - mutators (resolve and reject) methods removed. - - .. js:function:: Deferred.resolve(value…) - - Called to resolve a deferred, any value provided will be - passed onto the success handlers of the deferred object. - - Resolving a deferred which has already been resolved or - rejected has no effect. - - .. js:function:: Deferred.reject(value…) - - Called to reject (fail) a deferred, any value provided will be - passed onto the failure handler of the deferred object. - - Rejecting a deferred which has already been resolved or - rejected has no effect. - - .. js:function:: Deferred.pipe(doneFilter[, failFilter]) - - Filters the result of a deferred, able to transform a success - into failure and a failure into success, or to delay - resolution further. - -.. [#] or simply calling :js:class:`Deferred` as a function, the - result is the same - -.. [#] or not-promises, the `CommonJS Promises/B`_ role of - :js:func:`when` is to be able to treat values and promises - uniformly: :js:func:`when` will pass promises through directly, - but non-promise values and objects will be transformed into a - resolved promise (resolving themselves with the value itself). - - jQuery's :js:func:`when` keeps this behavior making deferreds - easy to build from "static" values, or allowing defensive code - where expected promises are wrapped in :js:func:`when` just in - case. - -.. _promises: http://en.wikipedia.org/wiki/Promise_(programming) -.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/ -.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A -.. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B -.. _mocks: http://en.wikipedia.org/wiki/Mock_object diff --git a/doc/changelog-7.0.rst b/doc/changelog-7.0.rst deleted file mode 100644 index 00d0236..0000000 --- a/doc/changelog-7.0.rst +++ /dev/null @@ -1,108 +0,0 @@ -API changes from OpenERP Web 6.1 to 7.0 -======================================= - -DataSet -> Model ----------------- - -The 6.1 ``DataSet`` API has been deprecated in favor of the smaller -and more orthogonal :doc:`Model ` API, which more closely -matches the API in OpenERP Web's Python side and in OpenObject addons -and removes most stateful behavior of DataSet. - -Migration guide -~~~~~~~~~~~~~~~ - -* Actual arbitrary RPC calls can just be remapped on a - :js:class:`~openerp.web.Model` instance: - - .. code-block:: javascript - - dataset.call(method, args) - - or - - .. code-block:: javascript - - dataset.call_and_eval(method, args) - - can be replaced by calls to :js:func:`openerp.web.Model.call`: - - .. code-block:: javascript - - model.call(method, args) - - If callbacks are passed directly to the older methods, they need to - be added to the new one via ``.then()``. - - .. note:: - - The ``context_index`` and ``domain_index`` features were not - ported, context and domain now need to be passed in "in full", - they won't be automatically filled with the user's current - context. - -* Shorcut methods (``name_get``, ``name_search``, ``unlink``, - ``write``, ...) should be ported to - :js:func:`openerp.web.Model.call`, using the server's original - signature. On the other hand, the non-shortcut equivalents can now - use keyword arguments (see :js:func:`~openerp.web.Model.call`'s - signature for details) - -* ``read_slice``, which allowed a single round-trip to perform a - search and a read, should be reimplemented via - :js:class:`~openerp.web.Query` objects (see: - :js:func:`~openerp.web.Model.query`) for clearer and simpler - code. ``read_index`` should be replaced by a - :js:class:`~openerp.web.Query` as well, combining - :js:func:`~openerp.web.Query.offset` and - :js:func:`~openerp.web.Query.first`. - -Rationale -~~~~~~~~~ - -Renaming - - The name *DataSet* exists in the CS community consciousness, and - (as its name implies) it's a set of data (often fetched from a - database, maybe lazily). OpenERP Web's dataset behaves very - differently as it does not store (much) data (only a bunch of ids - and just enough state to break things). The name "Model" matches - the one used on the Python side for the task of building an RPC - proxy to OpenERP objects. - -API simplification - - ``DataSet`` has a number of methods which serve as little more - than shortcuts, or are there due to domain and context evaluation - issues in 6.1. - - The shortcuts really add little value, and OpenERP Web 6.2 embeds - a restricted Python evaluator (in javascript) meaning most of the - context and domain parsing & evaluation can be moved to the - javascript code and does not require cooperative RPC bridging. - -DataGroup -> also Model ------------------------ - -Alongside the deprecation of ``DataSet`` for -:js:class:`~openerp.web.Model`, OpenERP Web 7.0 also deprecates -``DataGroup`` and its subtypes in favor of a single method on -:js:class:`~openerp.web.Query`: -:js:func:`~openerp.web.Query.group_by`. - -Migration guide -~~~~~~~~~~~~~~~ - -Rationale -~~~~~~~~~ - -While the ``DataGroup`` API worked (mostly), it is quite odd and -alien-looking, a bit too Smalltalk-inspired (behaves like a -self-contained flow-control structure for reasons which may or may not -have been good). - -Because it is heavily related to ``DataSet`` (as it *yields* -``DataSet`` objects), deprecating ``DataSet`` automatically deprecates -``DataGroup`` (if we want to stay consistent), which is a good time to -make the API more imperative and look more like what most developers -are used to. diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index 83fc969..0000000 --- a/doc/conf.py +++ /dev/null @@ -1,257 +0,0 @@ -# -*- coding: utf-8 -*- -# -# OpenERP Technical Documentation configuration file, created by -# sphinx-quickstart on Fri Feb 17 16:14:06 2012. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.abspath('_themes')) -sys.path.insert(0, os.path.abspath('../addons')) -sys.path.insert(0, os.path.abspath('..')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'OpenERP Web Developers Documentation' -copyright = u'2012, OpenERP s.a.' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '7.0' -# The full version, including alpha/beta/rc tags. -release = '7.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'flask' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['_themes'] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], - '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html', - 'sourcelink.html', 'searchbox.html'] -} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'openerp-web-doc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'openerp-web-doc.tex', u'OpenERP Web Developers Documentation', - u'OpenERP s.a.', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'openerp-web-doc', u'OpenERP Web Developers Documentation', - [u'OpenERP s.a.'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'OpenERPWebDocumentation', u'OpenERP Web Developers Documentation', - u'OpenERP s.a.', 'OpenERPWebDocumentation', 'Developers documentation for the openerp-web project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -todo_include_todos = True - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = { - 'python': ('http://docs.python.org/', None), - 'openerpserver': ('http://doc.openerp.com/trunk/developers/server', None), - 'openerpdev': ('http://doc.openerp.com/trunk/developers', None), -} diff --git a/doc/form-notes.rst b/doc/form-notes.rst deleted file mode 100644 index 98d4a4d..0000000 --- a/doc/form-notes.rst +++ /dev/null @@ -1,55 +0,0 @@ -Notes on the usage of the Form View as a sub-widget -=================================================== - -Undocumented stuff ------------------- - -* ``initial_mode`` *option* defines the starting mode of the form - view, one of ``view`` and ``edit`` (?). Default value is ``view`` - (non-editable form). - -* ``embedded_view`` *attribute* has to be set separately when - providing a view directly, no option available for that usage. - - * View arch **must** contain node with - ``@class="oe_form_container"``, otherwise everything will break - without any info - - * Root element of view arch not being ``form`` may or may not work - correctly, no idea. - - * Freeform views => ``@version="7.0"`` - -* Form is not entirely loaded (some widgets may not appear) unless - ``on_record_loaded`` is called (or ``do_show``, which itself calls - ``on_record_loaded``). - -* "Empty" form => ``on_button_new`` (...), or manually call - ``default_get`` + ``on_record_loaded`` - -* Form fields default to width: 100%, padding, !important margin, can - be reached via ``.oe_form_field`` - -* Form *will* render buttons and a pager, offers options to locate - both outside of form itself (``$buttons`` and ``$pager``), providing - empty jquery objects (``$()``) seems to stop displaying both but not - sure if there are deleterious side-effects. - - Other options: - - * Pass in ``$(document.createDocumentFragment)`` to ensure it's a - DOM-compatible tree completely outside of the actual DOM. - - * ??? - -* readonly fields probably don't have a background, beware if need of - overlay - - * What is the difference between ``readonly`` and - ``effective_readonly``? - -* No facilities for DOM events handling/delegations e.g. handling - keyup/keydown/keypress from a form fields into the form's user. - - * Also no way to reverse from a DOM node (e.g. DOMEvent#target) back to a - form view field easily diff --git a/doc/guides/client-action.rst b/doc/guides/client-action.rst deleted file mode 100644 index 30f0c7e..0000000 --- a/doc/guides/client-action.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. highlight:: javascript - -Creating a new client action -============================ - -Client actions are the client-side version of OpenERP's "Server -Actions": instead of allowing for semi-arbitrary code to be executed -in the server, they allow for execution of client-customized code. - -On the server side, a client action is an action of type -``ir.actions.client``, which has (at most) two properties: a mandatory -``tag``, which is an arbitrary string by which the client will -identify the action, and an optional ``params`` which is simply a map -of keys and values sent to the client as-is (this way, client actions -can be made generic and reused in multiple contexts). - -General Structure ------------------ - -In the OpenERP Web code, a client action only requires two pieces of -information: - -* Mapping the action's ``tag`` to an object - -* Providing said object. Two different types of objects can be mapped - to a client action: - - * An OpenERP Web widget, which must inherit from - :js:class:`openerp.web.Widget` - - * A regular javascript function - -The major difference is in the lifecycle of these: - -* if the client action maps to a function, the function will simply be - called when executing the action. The function can have no further - interaction with the Web Client itself, although it can return an - action which will be executed after it. - -* if, on the other hand, the client action maps to a - :js:class:`~openerp.web.Widget`, that - :js:class:`~openerp.web.Widget` will be instantiated and added to - the web client's canvas, with the usual - :js:class:`~openerp.web.Widget` lifecycle (essentially, it will - either take over the content area of the client or it will be - integrated within a dialog). - -For example, to create a client action displaying a ``res.widget`` -object:: - - // Registers the object 'openerp.web_dashboard.Widget' to the client - // action tag 'board.home.widgets' - instance.web.client_actions.add( - 'board.home.widgets', 'openerp.web_dashboard.Widget'); - instance.web_dashboard.Widget = instance.web.Widget.extend({ - template: 'HomeWidget' - }); - -At this point, the generic :js:class:`~openerp.web.Widget` lifecycle -takes over, the template is rendered, inserted in the client DOM, -bound on the object's ``$el`` property and the object is started. - -If the client action takes parameters, these parameters are passed in as a -second positional parameter to the constructor:: - - init: function (parent, params) { - // execute the Widget's init - this._super(parent); - // board.home.widgets only takes a single param, the identifier of the - // res.widget object it should display. Store it for later - this.widget_id = params.widget_id; - } - -More complex initialization (DOM manipulations, RPC requests, ...) -should be performed in the :js:func:`~openerp.web.Widget.start()` -method. - -.. note:: - - As required by :js:class:`~openerp.web.Widget`'s contract, if - :js:func:`~openerp.web.Widget.start()` executes any asynchronous - code it should return a ``$.Deferred`` so callers know when it's - ready for interaction. - - Although generally speaking client actions are not really - interacted with. - -.. code-block:: javascript - - start: function () { - return $.when( - this._super(), - // Simply read the res.widget object this action should display - new instance.web.Model('res.widget').call( - 'read', [[this.widget_id], ['title']]) - .then(this.proxy('on_widget_loaded')); - } - -The client action can then behave exactly as it wishes to within its -root (``this.$el``). In this case, it performs further renderings once -its widget's content is retrieved:: - - on_widget_loaded: function (widgets) { - var widget = widgets[0]; - var url = _.sprintf( - '/web_dashboard/widgets/content?session_id=%s&widget_id=%d', - this.session.session_id, widget.id); - this.$el.html(QWeb.render('HomeWidget.content', { - widget: widget, - url: url - })); - } diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 6a0d8de..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. OpenERP Web documentation master file, created by - sphinx-quickstart on Fri Mar 18 16:31:55 2011. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to OpenERP Web's documentation! -======================================= - -Contents: - -.. toctree:: - :maxdepth: 1 - - changelog-7.0 - - async - rpc - - widget - search-view - - list-view - form-notes - - guides/client-action - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/list-view.rst b/doc/list-view.rst deleted file mode 100644 index b7cdc1c..0000000 --- a/doc/list-view.rst +++ /dev/null @@ -1,531 +0,0 @@ -List View -========= - -Style Hooks ------------ - -The list view provides a few style hook classes for re-styling of list views in -various situations: - -``.oe_list`` - - The root element of the list view, styling rules should be rooted - on that class. - -``table.oe_list_content`` - - The root table for the listview, accessory components may be - generated or added outside this section, this is the list view - "proper". - -``.oe_list_buttons`` - - The action buttons array for the list view, with its sub-elements - - ``.oe_list_add`` - - The default "Create"/"Add" button of the list view - - ``.oe_alternative`` - - The "alternative choice" for the list view, by default text - along the lines of "or import" with a link. - -``.oe_list_field_cell`` - - The cell (``td``) for a given field of the list view, cells which - are *not* fields (e.g. name of a group, or number of items in a - group) will not have this class. The field cell can be further - specified: - - ``.oe_number`` - - Numeric cell types (integer and float) - - ``.oe_button`` - - Action button (``button`` tag in the view) inside the cell - - ``.oe_readonly`` - - Readonly field cell - - ``.oe_list_field_$type`` - - Additional class for the precise type of the cell, ``$type`` - is the field's @widget if there is one, otherwise it's the - field's type. - -``.oe_list_record_selector`` - - Selector cells - -Editable list view -++++++++++++++++++ - -The editable list view module adds a few supplementary style hook -classes, for edition situations: - -``.oe_list_editable`` - - Added to the ``.oe_list`` when the list is editable (however that - was done). The class may be removed on-the-fly if the list becomes - non-editable. - -``.oe_editing`` - - Added to both ``.oe_list`` and ``.oe_list_button`` (as the - buttons may be outside of the list view) when a row of the list is - currently being edited. - -``tr.oe_edition`` - - Class set on the row being edited itself. Note that the edition - form is *not* contained within the row, this allows for styling or - modifying the row while it's being edited separately. Mostly for - fields which can not be edited (e.g. read-only fields). - -Columns display customization ------------------------------ - -The list view provides a registry to -:js:class:`openerp.web.list.Column` objects allowing for the -customization of a column's display (e.g. so that a binary field is -rendered as a link to the binary file directly in the list view). - -The registry is ``instance.web.list.columns``, the keys are of the -form ``tag.type`` where ``tag`` can be ``field`` or ``button``, and -``type`` can be either the field's type or the field's ``@widget`` (in -the view). - -Most of the time, you'll want to define a ``tag.widget`` key -(e.g. ``field.progressbar``). - -.. js:class:: openerp.web.list.Column(id, tag, attrs) - - .. js:function:: openerp.web.list.Column.format(record_data, options) - - Top-level formatting method, returns an empty string if the - column is invisible (unless the ``process_modifiers=false`` - option is provided); returns ``options.value_if_empty`` or an - empty string if there is no value in the record for the - column. - - Otherwise calls :js:func:`~openerp.web.list.Column._format` - and returns its result. - - This method only needs to be overridden if the column has no - concept of values (and needs to bypass that check), for a - button for instance. - - Otherwise, custom columns should generally override - :js:func:`~openerp.web.list.Column._format` instead. - - :returns: String - - .. js:function:: openerp.web.list.Column._format(record_data, options) - - Never called directly, called if the column is visible and has - a value. - - The default implementation calls - :js:func:`~openerp.web.format_value` and htmlescapes the - result (via ``_.escape``). - - Note that the implementation of - :js:func:`~openerp.web.list.Column._format` *must* escape the - data provided to it, its output will *not* be escaped by - :js:func:`~openerp.web.list.Column.format`. - - :returns: String - -Editable list view ------------------- - -List view edition is an extension to the base listview providing the -capability of inline record edition by delegating to an embedded form -view. - -Editability status -++++++++++++++++++ - -The editability status of a list view can be queried through the -:js:func:`~openerp.web.ListView.editable` method, will return a falsy -value if the listview is not currently editable. - -The editability status is based on three flags: - -``tree/@editable`` - - If present, can be either ``"top"`` or ``"bottom"``. Either will - make the list view editable, with new records being respectively - created at the top or at the bottom of the view. - -``context.set_editable`` - - Boolean flag extracted from a search context (during the - :js:func:`~openerp.web.ListView.do_search`` handler), ``true`` - will make the view editable (from the top), ``false`` or the - absence of the flag is a noop. - -``defaults.editable`` - - Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or - ``"bottom"``, fallback for the list view if none of the previous - two flags are set. - -These three flags can only *make* a listview editable, they can *not* -override a previously set flag. To do that, a listview user should -instead cancel :ref:`the edit:before event `. - -The editable list view module adds a number of methods to the list -view, on top of implementing the :js:class:`EditorDelegate` protocol: - -Interaction Methods -+++++++++++++++++++ - -.. js:function:: openerp.web.ListView.ensure_saved - - Attempts to resolve the pending edition, if any, by saving the - edited row's current state. - - :returns: delegate resolving to all editions having been saved, or - rejected if a pending edition could not be saved - (e.g. validation failure) - -.. js:function:: openerp.web.ListView.start_edition([record][, options]) - - Starts editing the provided record inline, through an overlay form - view of editable fields in the record. - - If no record is provided, creates a new one according to the - editability configuration of the list view. - - This method resolves any pending edition when invoked, before - starting a new edition. - - :param record: record to edit, or null to create a new record - :type record: :js:class:`~openerp.web.list.Record` - :param EditOptions options: - :returns: delegate to the form used for the edition - -.. js:function:: openerp.web.ListView.save_edition - - Resolves the pending edition. - - :returns: delegate to the save being completed, resolves to an - object with two attributes ``created`` (flag indicating - whether the saved record was just created or was - updated) and ``record`` the reloaded record having been - edited. - -.. js:function:: openerp.web.ListView.cancel_edition([force=false]) - - Cancels pending edition, cleans up the list view in case of - creation (removes the empty record being created). - - :param Boolean force: doesn't check if the user has added any - data, discards the edition unconditionally - -Utility Methods -+++++++++++++++ - -.. js:function:: openerp.web.ListView.get_cells_for(row) - - Extracts the cells from a listview row, and puts them in a - {fieldname: cell} mapping for analysis and manipulation. - - :param jQuery row: - :rtype: Object - -.. js:function:: openerp.web.ListView.with_event(event_name, event, action[, args][, trigger_params]) - - Executes ``action`` in the context of the view's editor, - bracketing it with cancellable event signals. - - :param String event_name: base name for the bracketing event, will - be postfixed by ``:before`` and - ``:after`` before being called - (respectively before and after - ``action`` is executed) - :param Object event: object passed to the ``:before`` event - handlers. - :param Function action: function called with the view's editor as - its ``this``. May return a deferred. - :param Array args: arguments passed to ``action`` - :param Array trigger_params: arguments passed to the ``:after`` - event handler alongside the results - of ``action`` - -Behavioral Customizations -+++++++++++++++++++++++++ - -.. js:function:: openerp.web.ListView.handle_onwrite(record) - - Implements the handling of the ``onwrite`` listview attribute: - calls the RPC methods specified by ``@onwrite``, and if that - method returns an array of ids loads or reloads the records - corresponding to those ids. - - :param record: record being written having triggered the - ``onwrite`` callback - :type record: openerp.web.list.Record - :returns: deferred to all reloadings being done - -Events -++++++ - -For simpler interactions by/with external users of the listview, the -view provides a number of dedicated events to its lifecycle. - -.. note:: if an event is defined as *cancellable*, it means its first - parameter is an object on which the ``cancel`` attribute can - be set. If the ``cancel`` attribute is set, the view will - abort its current behavior as soon as possible, and rollback - any state modification. - - Generally speaking, an event should only be cancelled (by - setting the ``cancel`` flag to ``true``), uncancelling an - event is undefined as event handlers are executed on a - first-come-first-serve basis and later handlers may - re-cancel an uncancelled event. - -.. _listview-edit-before: - -``edit:before`` *cancellable* - - Invoked before the list view starts editing a record. - - Provided with an event object with a single property ``record``, - holding the attributes of the record being edited (``record`` is - empty *but not null* for a new record) - -``edit:after`` - - Invoked after the list view has gone into an edition state, - provided with the attributes of the record being edited (see - ``edit:before``) as first parameter and the form used for the - edition as second parameter. - -``save:before`` *cancellable* - - Invoked right before saving a pending edition, provided with an - event object holding the listview's editor (``editor``) and the - edition form (``form``) - -``save:after`` - - Invoked after a save has been completed - -``cancel:before`` *cancellable* - - Invoked before cancelling a pending edition, provided with the - same information as ``save:before``. - -``cancel:after`` - - Invoked after a pending edition has been cancelled. - -DOM events -++++++++++ - -The list view has grown hooks for the ``keyup`` event on its edition -form (during edition): any such event bubbling out of the edition form -will be forwarded to a method ``keyup_EVENTNAME``, where ``EVENTNAME`` -is the name of the key in ``$.ui.keyCode``. - -The method will also get the event object (originally passed to the -``keyup`` handler) as its sole parameter. - -The base editable list view has handlers for the ``ENTER`` and -``ESCAPE`` keys. - -Editor ------- - -The list-edition modules does not generally interact with the embedded -formview, delegating instead to its -:js:class:`~openerp.web.list.Editor`. - -.. js:class:: openerp.web.list.Editor(parent[, options]) - - The editor object provides a more convenient interface to form - views, and simplifies the usage of form views for semi-arbitrary - edition of stuff. - - However, the editor does *not* task itself with being internally - consistent at this point: calling - e.g. :js:func:`~openerp.web.list.Editor.edit` multiple times in a - row without saving or cancelling each edit is undefined. - - :param parent: - :type parent: :js:class:`~openerp.web.Widget` - :param EditorOptions options: - - .. js:function:: openerp.web.list.Editor.is_editing([record_state]) - - Indicates whether the editor is currently in the process of - providing edition for a record. - - Can be filtered by the state of the record being edited - (whether it's a record being *created* or a record being - *altered*), in which case it asserts both that an edition is - underway and that the record being edited respectively does - not yet exist in the database or already exists there. - - :param record_state: state of the record being edited. - Either ``"new"`` or ``"edit"``. - :type record_state: String - :rtype: Boolean - - .. js:function:: openerp.web.list.Editor.edit(record, configureField[, options]) - - Loads the provided record into the internal form view and - displays the form view. - - Will also attempt to focus the first visible field of the form - view. - - :param Object record: record to load into the form view - (key:value mapping similar to the result - of a ``read``) - :param configureField: function called with each field of the - form view right after the form is - displayed, lets whoever called this - method do some last-minute - configuration of form fields. - :type configureField: Function - :param EditOptions options: - :returns: jQuery delegate to the form object - - .. js:function:: openerp.web.list.Editor.save - - Attempts to save the internal form, then hide it - - :returns: delegate to the record under edition (with ``id`` - added for a creation). The record is not updated - from when it was passed in, aside from the ``id`` - attribute. - - .. js:function:: openerp.web.list.Editor.cancel([force=false]) - - Attemps to cancel the edition of the internal form, then hide - the form - - :param Boolean force: unconditionally cancels the edition of - the internal form, even if the user has - already entered data in it. - :returns: delegate to the record under edition - -.. js:class:: EditorOptions - - .. js:attribute:: EditorOptions.formView - - Form view (sub)-class to instantiate and delegate edition to. - - By default, :js:class:`~openerp.web.FormView` - - .. js:attribute:: EditorOptions.delegate - - Object used to get various bits of information about how to - display stuff. - - By default, uses the editor's parent widget. See - :js:class:`EditorDelegate` for the methods and attributes to - provide. - -.. js:class:: EditorDelegate - - Informal protocol defining the methods and attributes expected of - the :js:class:`~openerp.web.list.Editor`'s delegate. - - .. js:attribute:: EditorDelegate.dataset - - The dataset passed to the form view to synchronize the form - view and the outer widget. - - .. js:function:: EditorDelegate.edition_view(editor) - - Called by the :js:class:`~openerp.web.list.Editor` object to - get a form view (JSON) to pass along to the form view it - created. - - The result should be a valid form view, see :doc:`Form Notes - ` for various peculiarities of the form view - format. - - :param editor: editor object asking for the view - :type editor: :js:class:`~openerp.web.list.Editor` - :returns: form view - :rtype: Object - - .. js:function:: EditorDelegate.prepends_on_create - - By default, the :js:class:`~openerp.web.list.Editor` will - append the ids of newly created records to the - :js:attr:`EditorDelegate.dataset`. If this method returns - ``true``, it will prepend these ids instead. - - :returns: whether new records should be prepended to the - dataset (instead of appended) - :rtype: Boolean - - -.. js:class:: EditOptions - - Options object optionally passed into a method starting an edition - to configure its setup and behavior - - .. js:attribute:: focus_field - - Name of the field to set focus on after setting up the edition - of the record. - - If this option is not provided, or the requested field can not - be focused (invisible, readonly or not in the view), the first - visible non-readonly field is focused. - -Changes from 6.1 ----------------- - -* The editable listview behavior has been rewritten pretty much from - scratch, any code touching on editability will have to be modified - - * The overloading of :js:class:`~openerp.web.ListView.Groups` and - :js:class:`~openerp.web.ListView.List` for editability has been - drastically simplified, and most of the behavior has been moved to - the list view itself. Only - :js:func:`~openerp.web.ListView.List.row_clicked` is still - overridden. - - * A new method ``get_row_for(record) -> jQuery(tr) | null`` has been - added to both ListView.List and ListView.Group, it can be called - from the list view to get the table row matching a record (if such - a row exists). - -* :js:func:`~openerp.web.ListView.do_button_action`'s core behavior - has been split away to - :js:func:`~openerp.web.ListView.handle_button`. This allows bypassing - overrides of :js:func:`~openerp.web.ListView.do_button_action` in a - parent class. - - Ideally, :js:func:`~openerp.web.ListView.handle_button` should not be - overridden. - -* Modifiers handling has been improved (all modifiers information - should now be available through :js:func:`~Column.modifiers_for`, - not just ``invisible``) - -* Changed some handling of the list view's record: a record may now - have no id, and the listview will handle that correctly (for new - records being created) as well as correctly handle the ``id`` being - set. - -* Extended the internal collections structure of the list view with - `#find`_, `#succ`_ and `#pred`_. - -.. _#find: http://underscorejs.org/#find - -.. _#succ: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:succ - -.. _#pred: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:pred diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index 3e72cad..0000000 --- a/doc/make.bat +++ /dev/null @@ -1,170 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\OpenERPWeb.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\OpenERPWeb.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/doc/rpc.rst b/doc/rpc.rst deleted file mode 100644 index 4787978..0000000 --- a/doc/rpc.rst +++ /dev/null @@ -1,276 +0,0 @@ -Outside the box: network interactions -===================================== - -Building static displays is all nice and good and allows for neat -effects (and sometimes you're given data to display from third parties -so you don't have to make any effort), but a point generally comes -where you'll want to talk to the world and make some network requests. - -OpenERP Web provides two primary APIs to handle this, a low-level -JSON-RPC based API communicating with the Python section of OpenERP -Web (and of your addon, if you have a Python part) and a high-level -API above that allowing your code to talk directly to the OpenERP -server, using familiar-looking calls. - -All networking APIs are :doc:`asynchronous `. As a result, all -of them will return :js:class:`Deferred` objects (whether they resolve -those with values or not). Understanding how those work before before -moving on is probably necessary. - -High-level API: calling into OpenERP models -------------------------------------------- - -Access to OpenERP object methods (made available through XML-RPC from -the server) is done via the :js:class:`openerp.web.Model` class. This -class maps onto the OpenERP server objects via two primary methods, -:js:func:`~openerp.web.Model.call` and -:js:func:`~openerp.web.Model.query`. - -:js:func:`~openerp.web.Model.call` is a direct mapping to the -corresponding method of the OpenERP server object. Its usage is -similar to that of the OpenERP Model API, with three differences: - -* The interface is :doc:`asynchronous `, so instead of - returning results directly RPC method calls will return - :js:class:`Deferred` instances, which will themselves resolve to the - result of the matching RPC call. - -* Because ECMAScript 3/Javascript 1.5 doesnt feature any equivalent to - ``__getattr__`` or ``method_missing``, there needs to be an explicit - method to dispatch RPC methods. - -* No notion of pooler, the model proxy is instantiated where needed, - not fetched from an other (somewhat global) object - -.. code-block:: javascript - - var Users = new Model('res.users'); - - Users.call('change_password', ['oldpassword', 'newpassword'], - {context: some_context}).then(function (result) { - // do something with change_password result - }); - -:js:func:`~openerp.web.Model.query` is a shortcut for a builder-style -interface to searches (``search`` + ``read`` in OpenERP RPC terms). It -returns a :js:class:`~openerp.web.Query` object which is immutable but -allows building new :js:class:`~openerp.web.Query` instances from the -first one, adding new properties or modifiying the parent object's: - -.. code-block:: javascript - - Users.query(['name', 'login', 'user_email', 'signature']) - .filter([['active', '=', true], ['company_id', '=', main_company]]) - .limit(15) - .all().then(function (users) { - // do work with users records - }); - -The query is only actually performed when calling one of the query -serialization methods, :js:func:`~openerp.web.Query.all` and -:js:func:`~openerp.web.Query.first`. These methods will perform a new -RPC call every time they are called. - -For that reason, it's actually possible to keep "intermediate" queries -around and use them differently/add new specifications on them. - -.. js:class:: openerp.web.Model(name) - - .. js:attribute:: openerp.web.Model.name - - name of the OpenERP model this object is bound to - - .. js:function:: openerp.web.Model.call(method[, args][, kwargs]) - - Calls the ``method`` method of the current model, with the - provided positional and keyword arguments. - - :param String method: method to call over rpc on the - :js:attr:`~openerp.web.Model.name` - :param Array<> args: positional arguments to pass to the - method, optional - :param Object<> kwargs: keyword arguments to pass to the - method, optional - :rtype: Deferred<> - - .. js:function:: openerp.web.Model.query(fields) - - :param Array fields: list of fields to fetch during - the search - :returns: a :js:class:`~openerp.web.Query` object - representing the search to perform - -.. js:class:: openerp.web.Query(fields) - - The first set of methods is the "fetching" methods. They perform - RPC queries using the internal data of the object they're called - on. - - .. js:function:: openerp.web.Query.all() - - Fetches the result of the current - :js:class:`~openerp.web.Query` object's search. - - :rtype: Deferred> - - .. js:function:: openerp.web.Query.first() - - Fetches the **first** result of the current - :js:class:`~openerp.web.Query`, or ``null`` if the current - :js:class:`~openerp.web.Query` does have any result. - - :rtype: Deferred - - .. js:function:: openerp.web.Query.count() - - Fetches the number of records the current - :js:class:`~openerp.web.Query` would retrieve. - - :rtype: Deferred - - .. js:function:: openerp.web.Query.group_by(grouping...) - - Fetches the groups for the query, using the first specified - grouping parameter - - :param Array grouping: Lists the levels of grouping - asked of the server. Grouping - can actually be an array or - varargs. - :rtype: Deferred> | null - - The second set of methods is the "mutator" methods, they create a - **new** :js:class:`~openerp.web.Query` object with the relevant - (internal) attribute either augmented or replaced. - - .. js:function:: openerp.web.Query.context(ctx) - - Adds the provided ``ctx`` to the query, on top of any existing - context - - .. js:function:: openerp.web.Query.filter(domain) - - Adds the provided domain to the query, this domain is - ``AND``-ed to the existing query domain. - - .. js:function:: opeenrp.web.Query.offset(offset) - - Sets the provided offset on the query. The new offset - *replaces* the old one. - - .. js:function:: openerp.web.Query.limit(limit) - - Sets the provided limit on the query. The new limit *replaces* - the old one. - - .. js:function:: openerp.web.Query.order_by(fields…) - - Overrides the model's natural order with the provided field - specifications. Behaves much like Django's `QuerySet.order_by - `_: - - * Takes 1..n field names, in order of most to least importance - (the first field is the first sorting key). Fields are - provided as strings. - - * A field specifies an ascending order, unless it is prefixed - with the minus sign "``-``" in which case the field is used - in the descending order - - Divergences from Django's sorting include a lack of random sort - (``?`` field) and the inability to "drill down" into relations - for sorting. - -Aggregation (grouping) -~~~~~~~~~~~~~~~~~~~~~~ - -OpenERP has powerful grouping capacities, but they are kind-of strange -in that they're recursive, and level n+1 relies on data provided -directly by the grouping at level n. As a result, while ``read_group`` -works it's not a very intuitive API. - -OpenERP Web 7.0 eschews direct calls to ``read_group`` in favor of -calling a method of :js:class:`~openerp.web.Query`, `much in the way -it is one in SQLAlchemy -`_ [#]_: - -.. code-block:: javascript - - some_query.group_by(['field1', 'field2']).then(function (groups) { - // do things with the fetched groups - }); - -This method is asynchronous when provided with 1..n fields (to group -on) as argument, but it can also be called without any field (empty -fields collection or nothing at all). In this case, instead of -returning a Deferred object it will return ``null``. - -When grouping criterion come from a third-party and may or may not -list fields (e.g. could be an empty list), this provides two ways to -test the presence of actual subgroups (versus the need to perform a -regular query for records): - -* A check on ``group_by``'s result and two completely separate code - paths - - .. code-block:: javascript - - var groups; - if (groups = some_query.group_by(gby)) { - groups.then(function (gs) { - // groups - }); - } - // no groups - -* Or a more coherent code path using :js:func:`when`'s ability to - coerce values into deferreds: - - .. code-block:: javascript - - $.when(some_query.group_by(gby)).then(function (groups) { - if (!groups) { - // No grouping - } else { - // grouping, even if there are no groups (groups - // itself could be an empty array) - } - }); - -The result of a (successful) :js:func:`~openerp.web.Query.group_by` is -an array of :js:class:`~openerp.web.QueryGroup`. - -Low-level API: RPC calls to Python side ---------------------------------------- - -While the previous section is great for calling core OpenERP code -(models code), it does not work if you want to call the Python side of -OpenERP Web. - -For this, a lower-level API exists on on -:js:class:`~openerp.web.Connection` objects (usually available through -``openerp.connection``): the ``rpc`` method. - -This method simply takes an absolute path (which is the combination of -the Python controller's ``_cp_path`` attribute and the name of the -method you want to call) and a mapping of attributes to values (applied -as keyword arguments on the Python method [#]_). This function fetches -the return value of the Python methods, converted to JSON. - -For instance, to call the ``eval_domain_and_context`` of the -:class:`~web.controllers.main.Session` controller: - -.. code-block:: javascript - - openerp.connection.rpc('/web/session/eval_domain_and_context', { - domains: ds, - contexts: cs - }).then(function (result) { - // handle result - }); - -.. [#] with a small twist: SQLAlchemy's ``orm.query.Query.group_by`` - is not terminal, it returns a query which can still be altered. - -.. [#] except for ``context``, which is extracted and stored in the - request object itself. diff --git a/doc/search-view.rst b/doc/search-view.rst deleted file mode 100644 index 16a91c5..0000000 --- a/doc/search-view.rst +++ /dev/null @@ -1,549 +0,0 @@ -Search View -=========== - -OpenERP Web 7.0 implements a unified facets-based search view instead -of the previous form-like search view (composed of buttons and -multiple fields). The goal for this change is twofold: - -* Avoid the common issue of users confusing the search view with a - form view and trying to create their records through it (or entering - all their data, hitting the ``Create`` button expecting their record - to be created and losing everything). - -* Improve the looks and behaviors of the view, and the fit within - OpenERP Web's new design. - -The internal structure of the faceted search is inspired by -`VisualSearch `_ -[#previous]_. - -As does VisualSearch, the new search view is based on `Backbone`_ and -makes significant use of Backbone's models and collections (OpenERP -Web's widgets make a good replacement for Backbone's own views). As a -result, understanding the implementation details of the OpenERP Web 7 -search view also requires a basic understanding of Backbone's models, -collections and events. - -.. note:: - - This document may mention *fetching* data. This is a shortcut for - "returning a :js:class:`Deferred` to [whatever is being - fetched]". Unless further noted, the function or method may opt to - return nothing by fetching ``null`` (which can easily be done by - returning ``$.when(null)``, which simply wraps the ``null`` in a - Deferred). - -Working with the search view: creating new inputs -------------------------------------------------- - -The primary component of search views, as with all other OpenERP -views, are inputs. The search view has two types of inputs — filters -and fields — but only one is easly customizable: fields. - -The mapping from OpenERP field types (and widgets) to search view -objects is stored in the ``openerp.web.search.fields`` -:js:class:`~openerp.web.Registry` where new field types and widgets -can be added. - -Search view inputs have four main roles: - -Loading defaults -++++++++++++++++ - -Once the search view has initialized all its inputs, it will call -:js:func:`~openerp.web.search.Input.facet_for_defaults` on each input, -passing it a mapping (a javascript object) of ``name:value`` extracted -from the action's context. - -This method should fetch a :js:class:`~openerp.web.search.Facet` (or -an equivalent object) for the field's default value if applicable (if -a default value for the field is found in the ``defaults`` mapping). - -A default implementation is provided which checks if ``defaults`` -contains a non-falsy value for the field's ``@name`` and calls -:js:func:`openerp.web.search.Input.facet_for` with that value. - -There is no default implementation of -:js:func:`openerp.web.search.Input.facet_for` [#no_impl]_, but -:js:class:`openerp.web.search.Field` provides one, which uses the -value as-is to fetch a :js:class:`~openerp.web.search.Facet`. - -Providing completions -+++++++++++++++++++++ - -An important component of the new search view is the auto-completion -pane, and the task of providing completion items is delegated to -inputs through the :js:func:`~openerp.web.search.Input.complete` -method. - -This method should take a single argument (the string being typed by -the user) and should fetch an ``Array`` of possible completions -[#completion]_. - -A default implementation is provided which fetches nothing. - -A completion item is a javascript object with two keys (technically it -can have any number of keys, but only these two will be used by the -search view): - -``label`` - - The string which will be displayed in the completion pane. It may - be formatted using HTML (inline only), as a result if ``value`` is - interpolated into it it *must* be escaped. ``_.escape`` can be - used for this. - -``facet`` - - Either a :js:class:`~openerp.web.search.Facet` object or (more - commonly) the corresponding attributes object. This is the facet - which will be inserted into the search query if the completion - item is selected by the user. - -If the ``facet`` is not provided (not present, ``null``, ``undefined`` -or any other falsy value), the completion item will not be selectable -and will act as a section title of sort (the ``label`` will be -formatted differently). If an input *may* fetch multiple completion -items, it *should* prefix those with a section title using its own -name. This has no technical consequence but is clearer for users. - -Providing drawer/supplementary UI -+++++++++++++++++++++++++++++++++ - -For some inputs (fields or not), interaction via autocompletion may be -awkward or even impossible. - -These may opt to being rendered in a "drawer" as well or instead. In -that case, they will undergo the normal widget lifecycle and be -rendered inside the drawer. - -.. Found no good type-based way to handle this, since there is no MI - (so no type-tagging) and it's possible for both Field and non-Field - input to be put into the drawer, for whatever reason (e.g. some - sort of auto-detector completion item for date widgets, but a - second more usual calendar widget in the drawer for more - obvious/precise interactions) - -Any input can note its desire to be rendered in the drawer by -returning a truthy value from -:js:func:`~openerp.web.search.Input.in_drawer`. - -By default, :js:func:`~openerp.web.search.Input.in_drawer` returns the -value of :js:attr:`~openerp.web.search.Input._in_drawer`, which is -``false``. The behavior can be toggled either by redefining the -attribute to ``true`` (either on the class or on the input), or by -overriding :js:func:`~openerp.web.search.Input.in_drawer` itself. - -The input will be rendered in the full width of the drawer, it will be -started only once (per view). - -.. todo:: drawer API (if a widget wants to close the drawer in some - way), part of the low-level SearchView API/interactions? - - -.. todo:: handle filters and filter groups via a "driver" input which - dynamically collects, lays out and renders filters? => - exercises drawer thingies - -Converting from facet objects -+++++++++++++++++++++++++++++ - -Ultimately, the point of the search view is to allow searching. In -OpenERP this is done via :ref:`domains `. On -the other hand, the OpenERP Web 7 search view's state is modelled -after a collection of :js:class:`~openerp.web.search.Facet`, and each -field of a search view may have special requirements when it comes to -the domains it produces [#special]_. - -So there needs to be some way of mapping -:js:class:`~openerp.web.search.Facet` objects to OpenERP search data. - -This is done via an input's -:js:func:`~openerp.web.search.Input.get_domain` and -:js:func:`~openerp.web.search.Input.get_context`. Each takes a -:js:class:`~openerp.web.search.Facet` and returns whatever it's -supposed to generate (a domain or a context, respectively). Either can -return ``null`` if the current value does not map to a domain or -context, and can throw an :js:class:`~openerp.web.search.Invalid` -exception if the value is not valid at all for the field. - -.. note:: - - The :js:class:`~openerp.web.search.Facet` object can have any - number of values (from 1 upwards) - -.. note:: - - There is a third conversion method, - :js:func:`~openerp.web.search.Input.get_groupby`, which returns an - ``Array`` of groupby domains rather than a single context. At this - point, it is only implemented on (and used by) filters. - -Programmatic interactions: internal model ------------------------------------------ - -This new searchview is built around an instance of -:js:class:`~openerp.web.search.SearchQuery` available as -:js:attr:`openerp.web.SearchView.query`. - -The query is a `backbone collection`_ of -:js:class:`~openerp.web.search.Facet` objects, which can be interacted -with directly by external objects or search view controls -(e.g. widgets displayed in the drawer). - -.. js:class:: openerp.web.search.SearchQuery - - The current search query of the search view, provides convenience - behaviors for manipulating :js:class:`~openerp.web.search.Facet` - on top of the usual `backbone collection`_ methods. - - The query ensures all of its facets contain at least one - :js:class:`~openerp.web.search.FacetValue` instance. Otherwise, - the facet is automatically removed from the query. - - .. js:function:: openerp.web.search.SearchQuery.add(values, options) - - Overridden from the base ``add`` method so that adding a facet - which is *already* in the collection will merge the value of - the new facet into the old one rather than add a second facet - with different values. - - :param values: facet, facet attributes or array thereof - :returns: the collection itself - - .. js:function:: openerp.web.search.SearchQuery.toggle(value, options) - - Convenience method for toggling facet values in a query: - removes the values (through the facet itself) if they are - present, adds them if they are not. If the facet itself is not - in the collection, adds it automatically. - - A toggling is atomic: only one change event will be triggered - on the facet regardless of the number of values added to or - removed from the facet (if the facet already exists), and the - facet is only removed from the query if it has no value *at - the end* of the toggling. - - :param value: facet or facet attributes - :returns: the collection - -.. js:class:: openerp.web.search.Facet - - A `backbone model`_ representing a single facet of the current - research. May map to a search field, or to a more complex or - fuzzier input (e.g. a custom filter or an advanced search). - - .. js:attribute:: category - - The displayed name of the facet, as a ``String``. This is a - backbone model attribute. - - .. js:attribute:: field - - The :js:class:`~openerp.web.search.Input` instance which - originally created the facet [#facet-field]_, used to delegate - some operations (such as serializing the facet's values to - domains and contexts). This is a backbone model attribute. - - .. js:attribute:: values - - :js:class:`~openerp.web.search.FacetValues` as a javascript - attribute, stores all the values for the facet and helps - propagate their events to the facet. Is also available as a - backbone attribute (via ``#get`` and ``#set``) in which cases - it serializes to and deserializes from javascript arrays (via - ``Collection#toJSON`` and ``Collection#reset``). - - .. js:attribute:: [icon] - - optional, a single ASCII letter (a-z or A-Z) mapping to the - bundled mnmliconsRegular icon font. - - When a facet with an ``icon`` attribute is rendered, the icon - is displayed (in the icon font) in the first section of the - facet instead of the ``category``. - - By default, only filters make use of this facility. - -.. js:class:: openerp.web.search.FacetValues - - `Backbone collection`_ of - :js:class:`~openerp.web.search.FacetValue` instances. - -.. js:class:: openerp.web.search.FacetValue - - `Backbone model`_ representing a single value within a facet, - represents a pair of (displayed name, logical value). - - .. js:attribute:: label - - Backbone model attribute storing the "displayable" - representation of the value, visually output to the - user. Must be a string. - - .. js:attribute:: value - - Backbone model attribute storing the logical/internal value - (of itself), will be used by - :js:class:`~openerp.web.search.Input` to serialize to domains - and contexts. - - Can be of any type. - -Field services --------------- - -:js:class:`~openerp.web.search.Field` provides a default -implementation of :js:func:`~openerp.web.search.Input.get_domain` and -:js:func:`~openerp.web.search.Input.get_context` taking care of most -of the peculiarities pertaining to OpenERP's handling of fields in -search views. It also provides finer hooks to let developers of new -fields and widgets customize the behavior they want without -necessarily having to reimplement all of -:js:func:`~openerp.web.search.Input.get_domain` or -:js:func:`~openerp.web.search.Input.get_context`: - -.. js:function:: openerp.web.search.Field.get_context(facet) - - If the field has no ``@context``, simply returns - ``null``. Otherwise, calls - :js:func:`~openerp.web.search.Field.value_from` once for each - :js:class:`~openerp.web.search.FacetValue` of the current - :js:class:`~openerp.web.search.Facet` (in order to extract the - basic javascript object from the - :js:class:`~openerp.web.search.FacetValue` then evaluates - ``@context`` with each of these values set as ``self``, and - returns the union of all these contexts. - - :param facet: - :type facet: openerp.web.search.Facet - :returns: a context (literal or compound) - -.. js:function:: openerp.web.search.Field.get_domain(facet) - - If the field has no ``@filter_domain``, calls - :js:func:`~openerp.web.search.Field.make_domain` once with each - :js:class:`~openerp.web.search.FacetValue` of the current - :js:class:`~openerp.web.search.Facet` as well as the field's - ``@name`` and either its ``@operator`` or - :js:attr:`~openerp.web.search.Field.default_operator`. - - If the field has an ``@filter_value``, calls - :js:func:`~openerp.web.search.Field.value_from` once per - :js:class:`~openerp.web.search.FacetValue` and evaluates - ``@filter_value`` with each of these values set as ``self``. - - In either case, "ors" all of the resulting domains (using ``|``) - if there is more than one - :js:class:`~openerp.web.search.FacetValue` and returns the union - of the result. - - :param facet: - :type facet: openerp.web.search.Facet - :returns: a domain (literal or compound) - -.. js:function:: openerp.web.search.Field.make_domain(name, operator, facetValue) - - Builds a literal domain from the provided data. Calls - :js:func:`~openerp.web.search.Field.value_from` on the - :js:class:`~openerp.web.search.FacetValue` and evaluates and sets - it as the domain's third value, uses the other two parameters as - the first two values. - - Can be overridden to build more complex default domains. - - :param String name: the field's name - :param String operator: the operator to use in the field's domain - :param facetValue: - :type facetValue: openerp.web.search.FacetValue - :returns: Array<(String, String, Object)> - -.. js:function:: openerp.web.search.Field.value_from(facetValue) - - Extracts a "bare" javascript value from the provided - :js:class:`~openerp.web.search.FacetValue`, and returns it. - - The default implementation will simply return the ``value`` - backbone property of the argument. - - :param facetValue: - :type facetValue: openerp.web.search.FacetValue - :returns: Object - -.. js:attribute:: openerp.web.search.Field.default_operator - - Operator used to build a domain when a field has no ``@operator`` - or ``@filter_domain``. ``"="`` for - :js:class:`~openerp.web.search.Field` - -Arbitrary data storage ----------------------- - -:js:class:`~openerp.web.search.Facet` and -:js:class:`~openerp.web.search.FacetValue` objects (and structures) -provided by your widgets should never be altered by the search view -(or an other widget). This means you are free to add arbitrary fields -in these structures if you need to (because you have more complex -needs than the attributes described in this document). - -Ideally this should be avoided, but the possibility remains. - -Changes -------- - -.. todo:: merge in changelog instead? - -The displaying of the search view was significantly altered from -OpenERP Web 6.1 to OpenERP Web 7. - -As a result, while the external API used to interact with the search -view does not change many internal details — including the interaction -between the search view and its widgets — were significantly altered: - -Internal operations -+++++++++++++++++++ - -* :js:func:`openerp.web.SearchView.do_clear` has been removed -* :js:func:`openerp.web.SearchView.do_toggle_filter` has been removed - -Widgets API -+++++++++++ - -* :js:func:`openerp.web.search.Widget.render` has been removed - -* :js:func:`openerp.web.search.Widget.make_id` has been removed - -* Search field objects are not openerp widgets anymore, their - ``start`` is not generally called - -* :js:func:`~openerp.web.search.Input.clear` has been removed since - clearing the search view now simply consists of removing all search - facets - -* :js:func:`~openerp.web.search.Input.get_domain` and - :js:func:`~openerp.web.search.Input.get_context` now take a - :js:class:`~openerp.web.search.Facet` as parameter, from which it's - their job to get whatever value they want - -* :js:func:`~openerp.web.search.Input.get_groupby` has been added. It returns - an :js:class:`Array` of context-like constructs. By default, it does not do - anything in :js:class:`~openerp.web.search.Field` and it returns the various - contexts of its enabled filters in - :js:class:`~openerp.web.search.FilterGroup`. - -Filters -+++++++ - -* :js:func:`openerp.web.search.Filter.is_enabled` has been removed - -* :js:class:`~openerp.web.search.FilterGroup` instances are still - rendered (and started) in the "advanced search" drawer. - -Fields -++++++ - -* ``get_value`` has been replaced by - :js:func:`~openerp.web.search.Field.value_from` as it now takes a - :js:class:`~openerp.web.search.FacetValue` argument (instead of no - argument). It provides a default implementation returning the - ``value`` property of its argument. - -* The third argument to - :js:func:`~openerp.web.search.Field.make_domain` is now a - :js:class:`~openerp.web.search.FacetValue` so child classes have all - the information they need to derive the "right" resulting domain. - -Custom filters -++++++++++++++ - -Instead of being an intrinsic part of the search view, custom filters -are now a special case of filter groups. They are treated specially -still, but much less so than they used to be. - -Many To One -+++++++++++ - -* Because the autocompletion service is now provided by the search - view itself, - :js:func:`openerp.web.search.ManyToOneField.setup_autocomplete` has - been removed. - -Advanced Search -+++++++++++++++ - -* The advanced search is now a more standard - :js:class:`~openerp.web.search.Input` configured to be rendered in - the drawer. - -* :js:class:`~openerp.web.search.ExtendedSearchProposition.Field` are - now standard widgets, with the "right" behaviors (they don't rebind - their ``$element`` in ``start()``) - -* The ad-hoc optional setting of the openerp field descriptor on a - :js:class:`~openerp.web.search.ExtendedSearchProposition.Field` has - been removed, the field descriptor is now passed as second argument - to the - :js:class:`~openerp.web.search.ExtendedSearchProposition.Field`'s - constructor, and bound to its - :js:attr:`~openerp.web.search.ExtendedSearchProposition.Field.field`. - -* Instead of its former domain triplet ``(field, operator, value)``, - :js:func:`~openerp.web.search.ExtendedSearchProposition.get_proposition` - now returns an object with two fields ``label`` and ``value``, - respectively a human-readable version of the proposition and the - corresponding domain triplet for the proposition. - -.. [#previous] - - the original view was implemented on top of a monkey-patched - VisualSearch, but as our needs diverged from VisualSearch's goal - this made less and less sense ultimately leading to a clean-room - reimplementation - -.. [#no_impl] - - In case you are extending the search view with a brand new type of - input - -.. [#completion] - - Ideally this array should not hold more than about 10 items, but - the search view does not put any constraint on this at the - moment. Note that this may change. - -.. [#facet-field] - - ``field`` does not actually need to be an instance of - :js:class:`~openerp.web.search.Input`, nor does it need to be what - created the facet, it just needs to provide the three - facet-serialization methods - :js:func:`~openerp.web.search.Input.get_domain`, - :js:func:`~openerp.web.search.Input.get_context` and - :js:func:`~openerp.web.search.Input.get_gropuby`, existing - :js:class:`~openerp.web.search.Input` subtypes merely provide - convenient base implementation for those methods. - - Complex search view inputs (especially those living in the drawer) - may prefer using object literals with the right slots returning - closed-over values or some other scheme un-bound to an actual - :js:class:`~openerp.web.search.Input`, as - :js:class:`~openerp.web.search.CustomFilters` and - :js:class:`~openerp.web.search.Advanced` do. - -.. [#special] - - search view fields may also bundle context data to add to the - search context - -.. _Backbone: - http://documentcloud.github.com/backbone/ - -.. _Backbone.Collection: -.. _Backbone collection: - http://documentcloud.github.com/backbone/#Collection - -.. _Backbone model: - http://documentcloud.github.com/backbone/#Model - -.. _commit 3fca87101d: - https://github.com/documentcloud/visualsearch/commit/3fca87101d diff --git a/doc/widget.rst b/doc/widget.rst deleted file mode 100644 index ddfe525..0000000 --- a/doc/widget.rst +++ /dev/null @@ -1,274 +0,0 @@ -User Interaction: Widget -======================== - -This is the base class for all visual components. It corresponds to an MVC -view. It provides a number of services to handle a section of a page: - -* Rendering with QWeb - -* Parenting-child relations - -* Life-cycle management (including facilitating children destruction when a - parent object is removed) - -* DOM insertion, via jQuery-powered insertion methods. Insertion targets can - be anything the corresponding jQuery method accepts (generally selectors, - DOM nodes and jQuery objects): - - :js:func:`~openerp.base.Widget.appendTo` - Renders the widget and inserts it as the last child of the target, uses - `.appendTo()`_ - - :js:func:`~openerp.base.Widget.prependTo` - Renders the widget and inserts it as the first child of the target, uses - `.prependTo()`_ - - :js:func:`~openerp.base.Widget.insertAfter` - Renders the widget and inserts it as the preceding sibling of the target, - uses `.insertAfter()`_ - - :js:func:`~openerp.base.Widget.insertBefore` - Renders the widget and inserts it as the following sibling of the target, - uses `.insertBefore()`_ - -* Backbone-compatible shortcuts - -DOM Root --------- - -A :js:class:`~openerp.web.Widget` is responsible for a section of the -page materialized by the DOM root of the widget. The DOM root is -available via the :js:attr:`~openerp.web.Widget.el` and -:js:attr:`~openerp.web.Widget.$element` attributes, which are -respectively the raw DOM Element and the jQuery wrapper around the DOM -element. - -There are two main ways to define and generate this DOM root: - -.. js:attribute:: openerp.web.Widget.template - - Should be set to the name of a QWeb template (a - :js:class:`String`). If set, the template will be rendered after - the widget has been initialized but before it has been - started. The root element generated by the template will be set as - the DOM root of the widget. - -.. js:attribute:: openerp.web.Widget.tagName - - Used if the widget has no template defined. Defaults to ``div``, - will be used as the tag name to create the DOM element to set as - the widget's DOM root. It is possible to further customize this - generated DOM root with the following attributes: - - .. js:attribute:: openerp.web.Widget.id - - Used to generate an ``id`` attribute on the generated DOM - root. - - .. js:attribute:: openerp.web.Widget.className - - Used to generate a ``class`` attribute on the generated DOM root. - - .. js:attribute:: openerp.web.Widget.attributes - - Mapping (object literal) of attribute names to attribute - values. Each of these k:v pairs will be set as a DOM attribute - on the generated DOM root. - - None of these is used in case a template is specified on the widget. - -The DOM root can also be defined programmatically by overridding - -.. js:function:: openerp.web.Widget.renderElement - - Renders the widget's DOM root and sets it. The default - implementation will render a set template or generate an element - as described above, and will call - :js:func:`~openerp.web.Widget.setElement` on the result. - - Any override to :js:func:`~openerp.web.Widget.renderElement` which - does not call its ``_super`` **must** call - :js:func:`~openerp.web.Widget.setElement` with whatever it - generated or the widget's behavior is undefined.r - - .. note:: - - The default :js:func:`~openerp.web.Widget.renderElement` can - be called repeatedly, it will *replace* the previous DOM root - (using ``replaceWith``). However, this requires that the - widget correctly sets and unsets its events (and children - widgets). Generally, - :js:func:`~openerp.web.Widget.renderElement` should not be - called repeatedly unless the widget advertizes this feature. - -Accessing DOM content -~~~~~~~~~~~~~~~~~~~~~ - -Because a widget is only responsible for the content below its DOM -root, there is a shortcut for selecting sub-sections of a widget's -DOM: - -.. js:function:: openerp.web.Widget.$(selector) - - Applies the CSS selector specified as parameter to the widget's - DOM root. - - .. code-block:: javascript - - this.$(selector); - - is functionally identical to: - - .. code-block:: javascript - - this.$element.find(selector); - - :param String selector: CSS selector - :returns: jQuery object - - .. note:: this helper method is compatible with - ``Backbone.View.$`` - -Resetting the DOM root -~~~~~~~~~~~~~~~~~~~~~~ - -.. js:function:: openerp.web.Widget.setElement(element) - - Re-sets the widget's DOM root to the provided element, also - handles re-setting the various aliases of the DOM root as well as - unsetting and re-setting delegated events. - - :param Element element: a DOM element or jQuery object to set as - the widget's DOM root - - .. note:: should be mostly compatible with `Backbone's - setElement`_ - -DOM events handling -------------------- - -A widget will generally need to respond to user action within its -section of the page. This entails binding events to DOM elements. - -To this end, :js:class:`~openerp.web.Widget` provides an shortcut: - -.. js:attribute:: openerp.web.Widget.events - - Events are a mapping of ``event selector`` (an event name and a - CSS selector separated by a space) to a callback. The callback can - be either a method name in the widget or a function. In either - case, the ``this`` will be set to the widget. - - The selector is used for jQuery's `event delegation`_, the - callback will only be triggered for descendants of the DOM root - matching the selector [0]_. If the selector is left out (only an - event name is specified), the event will be set directly on the - widget's DOM root. - -.. js:function:: openerp.web.Widget.delegateEvents - - This method is in charge of binding - :js:attr:`~openerp.web.Widget.events` to the DOM. It is - automatically called after setting the widget's DOM root. - - It can be overridden to set up more complex events than the - :js:attr:`~openerp.web.Widget.events` map allows, but the parent - should always be called (or :js:attr:`~openerp.web.Widget.events` - won't be handled correctly). - -.. js:function:: openerp.web.Widget.undelegateEvents - - This method is in charge of unbinding - :js:attr:`~openerp.web.Widget.events` from the DOM root when the - widget is destroyed or the DOM root is reset, in order to avoid - leaving "phantom" events. - - It should be overridden to un-set any event set in an override of - :js:func:`~openerp.web.Widget.delegateEvents`. - -.. note:: this behavior should be compatible with `Backbone's - delegateEvents`_, apart from not accepting any argument. - -Subclassing Widget ------------------- - -:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the -:js:func:`~openerp.base.Class.extend` method), and provides a number of -abstract properties and concrete methods (which you may or may not want to -override). Creating a subclass looks like this: - -.. code-block:: javascript - - var MyWidget = openerp.base.Widget.extend({ - // QWeb template to use when rendering the object - template: "MyQWebTemplate", - - init: function(parent) { - this._super(parent); - // insert code to execute before rendering, for object - // initialization - }, - start: function() { - this._super(); - // post-rendering initialization code, at this point - // ``this.$element`` has been initialized - this.$element.find(".my_button").click(/* an example of event binding * /); - - // if ``start`` is asynchronous, return a promise object so callers - // know when the object is done initializing - return this.rpc(/* … */) - } - }); - -The new class can then be used in the following manner: - -.. code-block:: javascript - - // Create the instance - var my_widget = new MyWidget(this); - // Render and insert into DOM - my_widget.appendTo(".some-div"); - -After these two lines have executed (and any promise returned by ``appendTo`` -has been resolved if needed), the widget is ready to be used. - -.. note:: the insertion methods will start the widget themselves, and will - return the result of :js:func:`~openerp.base.Widget.start()`. - - If for some reason you do not want to call these methods, you will - have to first call :js:func:`~openerp.base.Widget.render()` on the - widget, then insert it into your DOM and start it. - -If the widget is not needed anymore (because it's transient), simply terminate -it: - -.. code-block:: javascript - - my_widget.destroy(); - -will unbind all DOM events, remove the widget's content from the DOM and -destroy all widget data. - -.. [0] not all DOM events are compatible with events delegation - -.. _.appendTo(): - http://api.jquery.com/appendTo/ - -.. _.prependTo(): - http://api.jquery.com/prependTo/ - -.. _.insertAfter(): - http://api.jquery.com/insertAfter/ - -.. _.insertBefore(): - http://api.jquery.com/insertBefore/ - -.. _event delegation: - http://api.jquery.com/delegate/ - -.. _Backbone's setElement: - http://backbonejs.org/#View-setElement - -.. _Backbone's delegateEvents: - http://backbonejs.org/#View-delegateEvents - diff --git a/logging.json b/logging.json deleted file mode 100644 index 1f982a6..0000000 --- a/logging.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 1, - "formatters": { - "simple": { - "format": "[%(asctime)s] %(levelname)s:%(name)s:%(message)s" - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "simple", - "stream": "ext://sys.stdout" - } - }, - "loggers": { - "web": { - }, - "web.common.openerplib": { - "level": "INFO" - } - }, - "root": { - "level": "DEBUG", - "handlers": ["console"] - } -} diff --git a/openerp-web b/openerp-web deleted file mode 100755 index 7e536a9..0000000 --- a/openerp-web +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python -import json -import logging -import logging.config -import optparse -import os -import sys -import tempfile - -import werkzeug.serving -import werkzeug.contrib.fixers - -optparser = optparse.OptionParser() -optparser.add_option("-s", "--session-path", dest="session_storage", - default=os.path.join(tempfile.gettempdir(), "oe-sessions"), - help="Directory used for session storage", metavar="DIR") -optparser.add_option("--server-host", dest="server_host", - default='127.0.0.1', help="OpenERP server hostname", metavar="HOST") -optparser.add_option("--server-port", dest="server_port", default=8069, - help="OpenERP server port", type="int", metavar="NUMBER") -optparser.add_option("--db-filter", dest="dbfilter", default='.*', - help="Filter listed databases", metavar="REGEXP") -optparser.add_option('--addons-path', dest='addons_path', default=[], action='append', - help="Path to addons directory", metavar="PATH") -optparser.add_option('--load', dest='server_wide_modules', default=['web'], action='append', - help="Load an additional module before login (by default only 'web' is loaded)", metavar="MODULE") - -server_options = optparse.OptionGroup(optparser, "Server configuration") -server_options.add_option("-p", "--port", dest="socket_port", default=8002, - help="listening port", type="int", metavar="NUMBER") -server_options.add_option('--reloader', dest='reloader', - default=False, action='store_true', - help="Reload application when python files change") -server_options.add_option('--no-serve-static', dest='serve_static', - default=True, action='store_false', - help="Do not serve static files via this server") -server_options.add_option('--multi-threaded', dest='threaded', - default=False, action='store_true', - help="Spawn one thread per HTTP request") -server_options.add_option('--proxy-mode', dest='proxy_mode', - default=False, action='store_true', - help="Enable correct behavior when behind a reverse proxy") -optparser.add_option_group(server_options) - -logging_opts = optparse.OptionGroup(optparser, "Logging") -logging_opts.add_option("--log-level", dest="log_level", type="choice", - default='debug', help="Global logging level", metavar="LOG_LEVEL", - choices=['debug', 'info', 'warning', 'error', 'critical']) -logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(os.path.dirname(__file__), "logging.json"), - help="Logging configuration file", metavar="FILE") -optparser.add_option_group(logging_opts) - -testing_opts = optparse.OptionGroup(optparser, "Testing") -testing_opts.add_option('--test-mode', dest='test_mode', - action='store_true', default=False, - help="Starts test mode, which provides a few" - " (utterly unsafe) APIs for testing purposes and" - " sets up a special connector which always raises" - " errors on tentative server access. These errors" - " serialize RPC query information (service," - " method, arguments list) in the fault_code" - " attribute of the error object returned to the" - " client. This lets javascript code assert the" \ - " XMLRPC consequences of its queries.") -optparser.add_option_group(testing_opts) - -if __name__ == "__main__": - (options, args) = optparser.parse_args(sys.argv[1:]) - - if not options.addons_path: - path_root = os.path.dirname(os.path.abspath(__file__)) - path_addons = os.path.join(path_root, 'addons') - if os.path.exists(path_addons): - options.addons_path.append(path_addons) - - options.addons_path = [ - path[:-1] if path[-1] in r'\/' else path - for path in options.addons_path - if os.path.exists(path) - ] - - for path_addons in options.addons_path: - if path_addons not in sys.path: - sys.path.insert(0, path_addons) - - try: - import web.common.http - except ImportError: - optparser.error('Error Importing base web module. Check correctness of --addons-path.') - - options.backend = 'xmlrpc' - os.environ["TZ"] = "UTC" - - if options.test_mode: - import web.test_support - import web.test_support.controllers - options.connector = web.test_support.TestConnector() - logging.getLogger('werkzeug').setLevel(logging.WARNING) - - if sys.version_info >= (2, 7) and os.path.exists(options.log_config): - with open(options.log_config) as file: - dct = json.load(file) - logging.config.dictConfig(dct) - logging.getLogger().setLevel(getattr(logging, options.log_level.upper())) - else: - logging.basicConfig(level=getattr(logging, options.log_level.upper())) - - app = web.common.http.Root(options) - - if options.proxy_mode: - app = werkzeug.contrib.fixers.ProxyFix(app) - - werkzeug.serving.run_simple( - '0.0.0.0', options.socket_port, app, - use_reloader=options.reloader, threaded=options.threaded) - diff --git a/openerp-web.cfg b/openerp-web.cfg deleted file mode 100644 index 424d1a8..0000000 --- a/openerp-web.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[global] - - diff --git a/setup.py b/setup.py deleted file mode 100755 index 18d3410..0000000 --- a/setup.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -import glob, os, re, setuptools, sys -from os.path import join, isfile - -# List all data files -def data(): - files = [] - for root, dirnames, filenames in os.walk('openerp'): - for filename in filenames: - if not re.match(r'.*(\.pyc|\.pyo|\~)$',filename): - files.append(os.path.join(root, filename)) - d = {} - for v in files: - k=os.path.dirname(v) - if k in d: - d[k].append(v) - else: - d[k]=[v] - r = d.items() - if os.name == 'nt': - r.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*'))) - - import babel - r.append(("localedata", - glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata" , '*')))) - - return r - -def gen_manifest(): - file_list="\n".join(data()) - open('MANIFEST','w').write(file_list) - -if os.name == 'nt': - sys.path.append("C:\Microsoft.VC90.CRT") - -def py2exe_options(): - if os.name == 'nt': - import py2exe - return { - "console" : [ { "script": "openerp-server", "icon_resources": [(1, join("install","openerp-icon.ico"))], }], - 'options' : { - "py2exe": { - "skip_archive": 1, - "optimize": 2, - "dist_dir": 'dist', - "packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "email", "encodings", "imaplib", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ], - "excludes" : ["Tkconstants","Tkinter","tcl"], - } - } - } - else: - return {} - -execfile(join(os.path.dirname(__file__), 'openerp', 'release.py')) - -setuptools.setup( - name = 'openerp', - version = version, - description = description, - long_description = long_desc, - url = url, - author = author, - author_email = author_email, - classifiers = filter(None, classifiers.split("\n")), - license = license, - scripts = ['openerp-server'], - data_files = data(), - packages = setuptools.find_packages(), - dependency_links = ['http://download.gna.org/pychart/'], - #include_package_data = True, - install_requires = [ - 'pychart', - 'babel', - 'docutils', - 'feedparser', - 'gdata', - 'lxml < 3', - 'mako', - 'psutil', - 'psycopg2', - 'pydot', - 'python-dateutil < 2', - 'python-ldap', - 'python-openid', - 'pytz', - 'pywebdav', - 'pyyaml', - 'reportlab', - 'simplejson', - 'vatnumber', - 'vobject', - 'werkzeug', - 'xlwt', - 'zsi', - ], - extras_require = { - 'SSL' : ['pyopenssl'], - }, - **py2exe_options() -) - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: