diff --git a/LICENSE.web b/LICENSE.web new file mode 100644 index 00000000000..dba13ed2ddf --- /dev/null +++ b/LICENSE.web @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.web b/README.web new file mode 100644 index 00000000000..7aff74c76c4 --- /dev/null +++ b/README.web @@ -0,0 +1,11 @@ +OpenERP Web +----------- + +The OpenERP Web Client supports the following web browsers: + +* Internet Explorer 9+ +* Google Chrome 22+ +* Firefox 13+ +* Any browser using the latest version of Chrome Frame + + diff --git a/addons/web/doc/async.rst b/addons/web/doc/async.rst index 23b3409bd8f..bd23d5a31d3 100644 --- a/addons/web/doc/async.rst +++ b/addons/web/doc/async.rst @@ -1,5 +1,5 @@ -Don't stop the world now: asynchronous development and Javascript -================================================================= +Asynchronous Operations +======================= As a language (and runtime), javascript is fundamentally single-threaded. This means any blocking request or computation will diff --git a/addons/web/doc/changelog-7.0.rst b/addons/web/doc/changelog-7.0.rst index dd08f1bcde7..b801c11b945 100644 --- a/addons/web/doc/changelog-7.0.rst +++ b/addons/web/doc/changelog-7.0.rst @@ -95,8 +95,8 @@ 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.Model`, OpenERP Web 7.0 removes +``DataGroup`` and its subtypes as public objects in favor of a single method on :js:class:`~openerp.web.Query`: :js:func:`~openerp.web.Query.group_by`. @@ -116,3 +116,8 @@ Because it is heavily related to ``DataSet`` (as it *yields* ``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. + +But as ``DataGroup`` users in 6.1 were rare (and there really was little reason +to use it), it has been removed as a public API. + + diff --git a/addons/web/doc/rpc.rst b/addons/web/doc/rpc.rst index 4787978e187..db6fcb4fa4b 100644 --- a/addons/web/doc/rpc.rst +++ b/addons/web/doc/rpc.rst @@ -1,5 +1,5 @@ -Outside the box: network interactions -===================================== +RPC Calls +========= Building static displays is all nice and good and allows for neat effects (and sometimes you're given data to display from third parties diff --git a/addons/web/i18n/ar.po b/addons/web/i18n/ar.po index 008f1d300d6..40756b45f6b 100644 --- a/addons/web/i18n/ar.po +++ b/addons/web/i18n/ar.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/bg.po b/addons/web/i18n/bg.po index e8b2d251e5d..d1ab853401a 100644 --- a/addons/web/i18n/bg.po +++ b/addons/web/i18n/bg.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/bn.po b/addons/web/i18n/bn.po index be55c854683..8b6accef4ac 100644 --- a/addons/web/i18n/bn.po +++ b/addons/web/i18n/bn.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/bs.po b/addons/web/i18n/bs.po index 9c4c8e1efea..e784c56a4fb 100644 --- a/addons/web/i18n/bs.po +++ b/addons/web/i18n/bs.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/ca.po b/addons/web/i18n/ca.po index d5377f66780..be31ecde6e8 100644 --- a/addons/web/i18n/ca.po +++ b/addons/web/i18n/ca.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/cs.po b/addons/web/i18n/cs.po index 1bc447a369e..2a580dfb6f1 100644 --- a/addons/web/i18n/cs.po +++ b/addons/web/i18n/cs.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" "X-Poedit-Language: Czech\n" #. openerp-web diff --git a/addons/web/i18n/da.po b/addons/web/i18n/da.po index 8799ce9472e..9ed2c394c67 100644 --- a/addons/web/i18n/da.po +++ b/addons/web/i18n/da.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/de.po b/addons/web/i18n/de.po index 3d1ff84113d..efffdaae086 100644 --- a/addons/web/i18n/de.po +++ b/addons/web/i18n/de.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/en_AU.po b/addons/web/i18n/en_AU.po index 833184dc94f..523091548a1 100644 --- a/addons/web/i18n/en_AU.po +++ b/addons/web/i18n/en_AU.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/en_GB.po b/addons/web/i18n/en_GB.po index d68113dbbac..eed11630b31 100644 --- a/addons/web/i18n/en_GB.po +++ b/addons/web/i18n/en_GB.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/es.po b/addons/web/i18n/es.po index e11bb829ba0..eb8d94b563c 100644 --- a/addons/web/i18n/es.po +++ b/addons/web/i18n/es.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/es_CL.po b/addons/web/i18n/es_CL.po index ef34d06b874..3888b799e52 100644 --- a/addons/web/i18n/es_CL.po +++ b/addons/web/i18n/es_CL.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/es_CR.po b/addons/web/i18n/es_CR.po index cb0bb1202e2..a3c9e0ea3f5 100644 --- a/addons/web/i18n/es_CR.po +++ b/addons/web/i18n/es_CR.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" "Language: es\n" #. openerp-web diff --git a/addons/web/i18n/es_EC.po b/addons/web/i18n/es_EC.po index d4ff6f80109..89aa15aa3f5 100644 --- a/addons/web/i18n/es_EC.po +++ b/addons/web/i18n/es_EC.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/et.po b/addons/web/i18n/et.po index 2422f46fc89..a664a0eba71 100644 --- a/addons/web/i18n/et.po +++ b/addons/web/i18n/et.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/eu.po b/addons/web/i18n/eu.po index 99debdd072d..ae886ab0b86 100644 --- a/addons/web/i18n/eu.po +++ b/addons/web/i18n/eu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/fi.po b/addons/web/i18n/fi.po index 43dbcdd8e18..e97a9cedfac 100644 --- a/addons/web/i18n/fi.po +++ b/addons/web/i18n/fi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/fr.po b/addons/web/i18n/fr.po index 619b8465dc5..036d1695bc4 100644 --- a/addons/web/i18n/fr.po +++ b/addons/web/i18n/fr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/fr_CA.po b/addons/web/i18n/fr_CA.po index 0f2cd756705..070e109a1ac 100644 --- a/addons/web/i18n/fr_CA.po +++ b/addons/web/i18n/fr_CA.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/gl.po b/addons/web/i18n/gl.po index 42d4e312372..6d3c4af4df7 100644 --- a/addons/web/i18n/gl.po +++ b/addons/web/i18n/gl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/gu.po b/addons/web/i18n/gu.po index 1d2f403e0db..851593765c3 100644 --- a/addons/web/i18n/gu.po +++ b/addons/web/i18n/gu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/hi.po b/addons/web/i18n/hi.po index 61b77e546c8..5e95732337f 100644 --- a/addons/web/i18n/hi.po +++ b/addons/web/i18n/hi.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/hr.po b/addons/web/i18n/hr.po index 3aadcd86171..47f2a6cb7ef 100644 --- a/addons/web/i18n/hr.po +++ b/addons/web/i18n/hr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/hu.po b/addons/web/i18n/hu.po index 691445417da..35722b9be95 100644 --- a/addons/web/i18n/hu.po +++ b/addons/web/i18n/hu.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/id.po b/addons/web/i18n/id.po index cf59fe32dba..4f5ff9a5b44 100644 --- a/addons/web/i18n/id.po +++ b/addons/web/i18n/id.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/it.po b/addons/web/i18n/it.po index 0ef8452b271..402ddad3365 100644 --- a/addons/web/i18n/it.po +++ b/addons/web/i18n/it.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/ja.po b/addons/web/i18n/ja.po index 699f55c1311..6fd63587a64 100644 --- a/addons/web/i18n/ja.po +++ b/addons/web/i18n/ja.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/ka.po b/addons/web/i18n/ka.po index 6798dca15c9..09c4ffd9f8b 100644 --- a/addons/web/i18n/ka.po +++ b/addons/web/i18n/ka.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/mk.po b/addons/web/i18n/mk.po index 613176f1c3c..9003b875eac 100644 --- a/addons/web/i18n/mk.po +++ b/addons/web/i18n/mk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/mn.po b/addons/web/i18n/mn.po index 58e8f985aec..072448c126d 100644 --- a/addons/web/i18n/mn.po +++ b/addons/web/i18n/mn.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/nb.po b/addons/web/i18n/nb.po index 0a82c8e5e39..275a678b617 100644 --- a/addons/web/i18n/nb.po +++ b/addons/web/i18n/nb.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/nl.po b/addons/web/i18n/nl.po index a99e02aeb7a..e23fe40a79b 100644 --- a/addons/web/i18n/nl.po +++ b/addons/web/i18n/nl.po @@ -9,13 +9,13 @@ msgstr "" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-07-02 09:06+0200\n" "PO-Revision-Date: 2012-07-02 14:46+0000\n" -"Last-Translator: Erwin \n" +"Last-Translator: Erwin van der Ploeg (Endian Solutions) \n" "Language-Team: Dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/nl_BE.po b/addons/web/i18n/nl_BE.po index cdf0bbdcee2..d7006b509a2 100644 --- a/addons/web/i18n/nl_BE.po +++ b/addons/web/i18n/nl_BE.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/pl.po b/addons/web/i18n/pl.po index 8881a0bc239..7a90d6d5432 100644 --- a/addons/web/i18n/pl.po +++ b/addons/web/i18n/pl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/pt.po b/addons/web/i18n/pt.po index 43b6d330612..d4fd51fa982 100644 --- a/addons/web/i18n/pt.po +++ b/addons/web/i18n/pt.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/pt_BR.po b/addons/web/i18n/pt_BR.po index 041c1d859d0..735c8f9feef 100644 --- a/addons/web/i18n/pt_BR.po +++ b/addons/web/i18n/pt_BR.po @@ -15,8 +15,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/ro.po b/addons/web/i18n/ro.po index ec52b3807e8..71c7cb15c58 100644 --- a/addons/web/i18n/ro.po +++ b/addons/web/i18n/ro.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/ru.po b/addons/web/i18n/ru.po index 6ed05f29c2a..a83821b5e5f 100644 --- a/addons/web/i18n/ru.po +++ b/addons/web/i18n/ru.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/sk.po b/addons/web/i18n/sk.po index 5656d604668..6a2165efaf9 100644 --- a/addons/web/i18n/sk.po +++ b/addons/web/i18n/sk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/sl.po b/addons/web/i18n/sl.po index d228cd9651e..a869b97eab4 100644 --- a/addons/web/i18n/sl.po +++ b/addons/web/i18n/sl.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/sq.po b/addons/web/i18n/sq.po index c77bd50961a..3b5e2ca41ea 100644 --- a/addons/web/i18n/sq.po +++ b/addons/web/i18n/sq.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:48+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/sr@latin.po b/addons/web/i18n/sr@latin.po index c0e6c7f6207..2c35afc3bde 100644 --- a/addons/web/i18n/sr@latin.po +++ b/addons/web/i18n/sr@latin.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/sv.po b/addons/web/i18n/sv.po index 1c5823e8041..978e4fb1fb3 100644 --- a/addons/web/i18n/sv.po +++ b/addons/web/i18n/sv.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/tr.po b/addons/web/i18n/tr.po index 2322b869344..7a74eba5dd0 100644 --- a/addons/web/i18n/tr.po +++ b/addons/web/i18n/tr.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/uk.po b/addons/web/i18n/uk.po index e45cf1dca3a..daed408aaaf 100644 --- a/addons/web/i18n/uk.po +++ b/addons/web/i18n/uk.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:27+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/zh_CN.po b/addons/web/i18n/zh_CN.po index 550ec9dc897..7539b136e03 100644 --- a/addons/web/i18n/zh_CN.po +++ b/addons/web/i18n/zh_CN.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/i18n/zh_TW.po b/addons/web/i18n/zh_TW.po index 0bdd2804510..f29b9002d8c 100644 --- a/addons/web/i18n/zh_TW.po +++ b/addons/web/i18n/zh_TW.po @@ -14,8 +14,8 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-10-03 05:28+0000\n" -"X-Generator: Launchpad (build 16061)\n" +"X-Launchpad-Export-Date: 2012-10-13 04:49+0000\n" +"X-Generator: Launchpad (build 16137)\n" #. openerp-web #: addons/web/static/src/js/chrome.js:176 diff --git a/addons/web/static/lib/datejs/globalization/ab-RU.js b/addons/web/static/lib/datejs/globalization/ab-RU.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/ar-AR.js b/addons/web/static/lib/datejs/globalization/ar-AR.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/bs-BS.js b/addons/web/static/lib/datejs/globalization/bs-BS.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/iu-CA.js b/addons/web/static/lib/datejs/globalization/iu-CA.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/ko-KP.js b/addons/web/static/lib/datejs/globalization/ko-KP.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/ml-IN.js b/addons/web/static/lib/datejs/globalization/ml-IN.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/oc-FR.js b/addons/web/static/lib/datejs/globalization/oc-FR.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/si-LK.js b/addons/web/static/lib/datejs/globalization/si-LK.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/sr-RS.js b/addons/web/static/lib/datejs/globalization/sr-RS.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/sr@latin.js b/addons/web/static/lib/datejs/globalization/sr@latin.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/lib/datejs/globalization/tlh-TLH.js b/addons/web/static/lib/datejs/globalization/tlh-TLH.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index c8f1567bee9..8925c3a0964 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -25,6 +25,7 @@ display: none !important; } } + .openerp.openerp_webclient_container { height: 100%; position: relative; @@ -1184,7 +1185,7 @@ color: white; padding: 2px 4px; margin: 1px 6px 0 0; - border: 1px solid lightgrey; + border: 1px solid lightGray; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -1209,7 +1210,7 @@ transform: scale(1.1); } .openerp .oe_secondary_submenu .oe_active { - border-top: 1px solid lightgrey; + border-top: 1px solid lightGray; border-bottom: 1px solid #dedede; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2); @@ -2054,36 +2055,36 @@ width: 400px; padding-bottom: 0; } -.openerp .oe_form header .oe_tags div.oe_chatter { +.openerp .oe_form div.oe_chatter { min-width: 650px; max-width: 860px; margin: 0 auto; padding: 16px 0 48px; } -.openerp .oe_form header .oe_tags div.oe_form_configuration p, .openerp .oe_form header .oe_tags div.oe_form_configuration ul, .openerp .oe_form header .oe_tags div.oe_form_configuration ol { +.openerp .oe_form div.oe_form_configuration p, .openerp .oe_form div.oe_form_configuration ul, .openerp .oe_form div.oe_form_configuration ol { color: #aaaaaa; max-width: 650px; } -.openerp .oe_form header .oe_tags div.oe_form_configuration label { +.openerp .oe_form div.oe_form_configuration label { min-width: 150px; } -.openerp .oe_form header .oe_tags div.oe_form_configuration .oe_form_group_cell_label { +.openerp .oe_form div.oe_form_configuration .oe_form_group_cell_label { padding: 1px 0; } -.openerp .oe_form header .oe_tags div.oe_form_configuration .oe_form_group_cell div div { +.openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div { padding: 1px 0; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer { +.openerp .oe_form .oe_subtotal_footer { width: 1% !important; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer td.oe_form_group_cell { +.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell { text-align: right; padding: 0 !important; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer td.oe_form_group_cell_label { +.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell_label { border-right: none; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer .oe_subtotal_footer_separator { +.openerp .oe_form .oe_subtotal_footer .oe_subtotal_footer_separator { width: 108px; border-top: 1px solid #cacaca; margin-top: 4px; @@ -2091,14 +2092,14 @@ font-weight: bold; font-size: 18px; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer label:after { +.openerp .oe_form .oe_subtotal_footer label:after { content: ":"; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer label.oe_subtotal_footer_separator { +.openerp .oe_form .oe_subtotal_footer label.oe_subtotal_footer_separator { font-weight: bold !important; padding: 2px 11px 2px 0px !important; } -.openerp .oe_form header .oe_tags .oe_subtotal_footer label.oe_form_label_help { +.openerp .oe_form .oe_subtotal_footer label.oe_form_label_help { font-weight: normal; } .openerp .oe_form .oe_form_button { @@ -2130,7 +2131,7 @@ } .openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span { font-size: 80%; - color: darkgreen; + color: darkGreen; vertical-align: top; position: relative; top: -4px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 823ced4e24f..a1adfe67e75 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1616,16 +1616,18 @@ $sheet-max-width: 860px ul display: inline-block float: right - .oe_button, + .oe_button margin: 3px 2px 1px &:first-child margin-left: 6px // }}} // FormView.custom tags and classes {{{ - .oe_form header .oe_tags - margin: 5px 0 0 5px - width: 400px - padding-bottom: 0 + .oe_form + header + .oe_tags + margin: 5px 0 0 5px + width: 400px + padding-bottom: 0 div.oe_chatter min-width: 650px max-width: $sheet-max-width diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index b605f4ad4e1..a298ac67159 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -70,7 +70,9 @@ instance.web.Dialog = instance.web.Widget.extend({ autoOpen: false, position: [false, 40], buttons: {}, - beforeClose: function () { self.on_close(); }, + beforeClose: function () { + self.trigger("closing"); + }, resizeStop: this.on_resized }; for (var f in this) { @@ -84,6 +86,7 @@ instance.web.Dialog = instance.web.Widget.extend({ } _.extend(this.dialog_options, options); } + this.on("closing", this, this._closing); }, get_options: function(options) { var self = this, @@ -126,13 +129,10 @@ instance.web.Dialog = instance.web.Widget.extend({ if (! this.dialog_inited) this.init_dialog(); var o = this.get_options(options); - if (! this.params_buttons) { - this.$buttons.appendTo($("body")); - } + this.$buttons.appendTo($("body")); instance.web.dialog(this.$el, o).dialog('open'); - if (! this.params_buttons) { - this.$buttons.appendTo(this.$el.dialog("widget")); - } + this.$el.dialog("widget").find(".ui-dialog-buttonpane").remove(); + this.$buttons.appendTo(this.$el.dialog("widget")); if (o.height === 'auto' && o.max_height) { this.$el.css({ 'max-height': o.max_height, 'overflow-y': 'auto' }); } @@ -145,6 +145,8 @@ instance.web.Dialog = instance.web.Widget.extend({ if (! this.params_buttons) { this.$buttons = $('
'); this.$el.dialog("widget").append(this.$buttons); + } else { + this.$buttons = this.$el.dialog("widget").find(".ui-dialog-buttonpane"); } this.dialog_inited = true; var res = this.start(); @@ -153,7 +155,7 @@ instance.web.Dialog = instance.web.Widget.extend({ close: function() { this.$el.dialog('close'); }, - on_close: function() { + _closing: function() { if (this.__tmp_dialog_destroying) return; if (this.dialog_options.destroy_on_close) { @@ -627,6 +629,7 @@ instance.web.Reload = function(parent, params) { hash = "#menu_id=" + menu_id; } var url = l.protocol + "//" + l.host + l.pathname + search + hash; + window.onerror = function() {}; window.location = url; }; instance.web.client_actions.add("reload", "instance.web.Reload"); @@ -878,9 +881,8 @@ instance.web.UserMenu = instance.web.Widget.extend({ }; this.update_promise = this.update_promise.pipe(fct, fct); }, - on_action: function() { - }, on_menu_logout: function() { + this.trigger('user_logout'); }, on_menu_settings: function() { var self = this; @@ -897,8 +899,7 @@ instance.web.UserMenu = instance.web.Widget.extend({ var $help = $(QWeb.render("UserMenu.about", {version_info: res})); $help.find('a.oe_activate_debug_mode').click(function (e) { e.preventDefault(); - window.location = $.param.querystring( - window.location.href, 'debug'); + window.location = $.param.querystring( window.location.href, 'debug'); }); instance.web.dialog($help, {autoOpen: true, modal: true, width: 507, height: 290, resizable: false, title: _t("About")}); @@ -1019,15 +1020,13 @@ instance.web.WebClient = instance.web.Client.extend({ var state = $.bbq.getState(true); var action = { - 'type': 'ir.actions.client', - 'tag': 'login', - 'params': state + type: 'ir.actions.client', + tag: 'login', + _push_me: false, }; this.action_manager.do_action(action); this.action_manager.inner_widget.on('login_successful', this, function() { - this.do_push_state(state); - this._current_state = null; // ensure the state will be loaded this.show_application(); // will load the state we just pushed }); }, @@ -1039,8 +1038,7 @@ instance.web.WebClient = instance.web.Client.extend({ self.menu.on('menu_click', this, this.on_menu_action); self.user_menu = new instance.web.UserMenu(self); self.user_menu.replace(this.$el.find('.oe_user_menu_placeholder')); - self.user_menu.on_menu_logout.add(this.proxy('on_logout')); - self.user_menu.on_action.add(this.proxy('on_menu_action')); + self.user_menu.on('user_logout', self, self.on_logout); self.user_menu.do_update(); self.bind_hashchange(); self.set_title(); @@ -1103,6 +1101,7 @@ instance.web.WebClient = instance.web.Client.extend({ }); }); } else { + state._push_me = false; // no need to push state back... this.action_manager.do_load_state(state, !!this._current_state); } } @@ -1121,9 +1120,11 @@ instance.web.WebClient = instance.web.Client.extend({ .pipe(function (result) { var action = result; if (options.needaction) { - action.context.search_default_needaction_pending = true; + action.context.search_default_message_unread = true; } - return $.when(self.action_manager.do_action(action, null, true)).fail(function() { + return $.when(self.action_manager.do_action(action, { + clear_breadcrumbs: true, + })).fail(function() { self.menu.open_menu(options.previous_menu_id); }); }); diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index b5fbacc7927..7f4f8c0d6ca 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -291,6 +291,17 @@ var Events = instance.web.Class.extend({ return this; }, + callbackList: function() { + var lst = []; + _.each(this._callbacks || {}, function(el, eventName) { + var node = el; + while ((node = node.next) && node.next) { + lst.push([eventName, node.callback, node.context]); + } + }); + return lst; + }, + trigger : function(events) { var event, node, calls, tail, args, all, rest; if (!(calls = this._callbacks)) @@ -369,9 +380,9 @@ instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, { event.source.__edispatcherEvents.off(event.name, event.func, self); }); this.__edispatcherRegisteredEvents = []; - if(!this.__edispatcherEvents) { - debugger; - } + _.each(this.__edispatcherEvents.callbackList(), function(cal) { + this.off(cal[0], cal[2], cal[1]); + }, this); this.__edispatcherEvents.off(); instance.web.ParentedMixin.destroy.call(this); } @@ -1440,6 +1451,10 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ }, }); +instance.web.py_eval = function(expr, context) { + return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null})); +}; + } // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index ce85e0ac43f..77583899bf1 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -280,7 +280,8 @@ instance.web.Model = instance.web.Class.extend({ kwargs = args; args = []; } - return instance.session.rpc('/web/dataset/call_kw', { + var debug = instance.session.debug ? '/'+this.name+':'+method : ''; + return instance.session.rpc('/web/dataset/call_kw' + debug, { model: this.name, method: method, args: args, @@ -349,89 +350,6 @@ instance.web.Model = instance.web.Class.extend({ }, }); -instance.web.DataGroup = instance.web.CallbackEnabled.extend({ - /** - * Management interface between views and grouped collections of OpenERP - * records. - * - * The root DataGroup is instantiated with the relevant information - * (a session, a model, a domain, a context and a group_by sequence), the - * domain and context may be empty. It is then interacted with via - * :js:func:`~instance.web.DataGroup.list`, which is used to read the - * content of the current grouping level. - * - * @constructs instance.web.DataGroup - * @extends instance.web.CallbackEnabled - * - * @param {instance.web.CallbackEnabled} parent widget - * @param {String} model name of the model managed by this DataGroup - * @param {Array} domain search domain for this DataGroup - * @param {Object} context context of the DataGroup's searches - * @param {Array} group_by sequence of fields by which to group - * @param {Number} [level=0] nesting level of the group - */ - init: function(parent, model, domain, context, group_by, level) { - this._super(parent, null); - this.model = new instance.web.Model(model, context, domain); - this.group_by = group_by; - this.context = context; - this.domain = domain; - - this.level = level || 0; - }, - list: function (fields, ifGroups, ifRecords) { - var self = this; - var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by); - $.when(query).then(function (querygroups) { - // leaf node - if (!querygroups) { - var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain()); - ds._sort = self.sort; - ifRecords(ds); - return; - } - // internal node - var child_datagroups = _(querygroups).map(function (group) { - var child_context = _.extend( - {}, self.model.context(), group.model.context()); - var child_dg = new instance.web.DataGroup( - self, self.model.name, group.model.domain(), - child_context, group.model._context.group_by, - self.level + 1); - child_dg.sort = self.sort; - // copy querygroup properties - child_dg.__context = child_context; - child_dg.__domain = group.model.domain(); - child_dg.folded = group.get('folded'); - child_dg.grouped_on = group.get('grouped_on'); - child_dg.length = group.get('length'); - child_dg.value = group.get('value'); - child_dg.openable = group.get('has_children'); - child_dg.aggregates = group.get('aggregates'); - return child_dg; - }); - ifGroups(child_datagroups); - }); - } -}); - -instance.web.StaticDataGroup = instance.web.DataGroup.extend({ - /** - * A specialization of data groups, relying on a single static - * dataset as its records provider. - * - * @constructs instance.web.StaticDataGroup - * @extends instance.web.DataGroup - * @param {openep.web.DataSetStatic} dataset a static dataset backing the groups - */ - init: function (dataset) { - this.dataset = dataset; - }, - list: function (fields, ifGroups, ifRecords) { - ifRecords(this.dataset); - } -}); - instance.web.DataSet = instance.web.CallbackEnabled.extend({ /** * Collection of OpenERP records, used to share records and the current selection between views. @@ -567,7 +485,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({ */ write: function (id, data, options) { options = options || {}; - return this._model.call('write', [[id], data], {context: this._model.context(options.context)}); + return this._model.call('write', [[id], data], {context: this._model.context(options.context)}).then(this.trigger('dataset_changed', id, data, options)); }, /** * Deletes an existing record from the database @@ -575,8 +493,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({ * @param {Number|String} ids identifier of the record to delete */ unlink: function(ids) { - this.trigger('unlink', ids); - return this._model.call('unlink', [ids], {context: this._model.context()}); + return this._model.call('unlink', [ids], {context: this._model.context()}).then(this.trigger('dataset_changed', ids)); }, /** * Calls an arbitrary RPC method @@ -774,10 +691,11 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({ if (self._length) { self._length -= 1; } - if (this.index !== null) { + if (self.index !== null) { self.index = self.index <= self.ids.length - 1 ? self.index : (self.ids.length > 0 ? self.ids.length -1 : 0); } + self.trigger("dataset_changed", ids, callback, error_callback); }); }, size: function () { @@ -834,7 +752,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({ } $.extend(cached.values, record.values); if (dirty) - this.on_change(); + this.trigger("dataset_changed", id, data, options); return $.Deferred().resolve(true).promise(); }, unlink: function(ids, callback, error_callback) { @@ -848,7 +766,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({ this.to_write = _.reject(this.to_write, function(x) { return _.include(ids, x.id);}); this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);}); this.set_ids(_.without.apply(_, [this.ids].concat(ids))); - this.on_change(); + this.trigger("dataset_changed", ids, callback, error_callback); return $.async_when({result: true}).then(callback); }, reset_ids: function(ids) { @@ -859,8 +777,6 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({ this.cache = []; this.delete_all = false; }, - on_change: function() { - }, read_ids: function (ids, fields, options) { var self = this; var to_get = []; @@ -947,7 +863,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({ }, alter_ids: function(n_ids) { this._super(n_ids); - this.on_change(); + this.trigger("dataset_changed", n_ids); }, }); instance.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/; diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index 3e5e6fecd5e..03b2c5b775d 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -135,7 +135,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) { Math.round((value % 1) * 60)); case 'many2one': // name_get value format - return value[1]; + return value[1] ? value[1].split("\n")[0] : value[1]; case 'one2many': case 'many2many': if (typeof value === 'string') { diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index d9254d2d724..606d966da3f 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -110,6 +110,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM "not_interactible_on_create": false, "initial_mode": "view", "disable_autofocus": false, + "footer_to_buttons": false, }); this.is_initialized = $.Deferred(); this.mutating_mutex = new $.Mutex(); @@ -118,7 +119,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.__clicked_inside = false; this.__blur_timeout = null; this.rendering_engine = new instance.web.form.FormRenderingEngine(this); - this.qweb = null; // A QWeb instance will be created if the view is a QWeb template self.set({actual_mode: self.options.initial_mode}); this.has_been_loaded.then(function() { self.on("change:actual_mode", self, self.check_actual_mode); @@ -157,11 +157,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.rendering_engine.set_fields_registry(this.fields_registry); this.rendering_engine.set_tags_registry(this.tags_registry); this.rendering_engine.set_widgets_registry(this.widgets_registry); - if (!this.extract_qweb_template(data)) { - this.rendering_engine.set_fields_view(data); - var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container'); - this.rendering_engine.render_to($dest); - } + this.rendering_engine.set_fields_view(data); + var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container'); + this.rendering_engine.render_to($dest); this.$el.on('mousedown.formBlur', function () { self.__clicked_inside = true; @@ -169,6 +167,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self})); if (this.options.$buttons) { + this.options.$buttons.children().remove(); this.$buttons.appendTo(this.options.$buttons); } else { this.$el.find('.oe_form_buttons').replaceWith(this.$buttons); @@ -177,6 +176,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.$buttons.on('click', '.oe_form_button_edit', this.on_button_edit); this.$buttons.on('click', '.oe_form_button_save', this.on_button_save); this.$buttons.on('click', '.oe_form_button_cancel', this.on_button_cancel); + if (this.options.footer_to_buttons) { + this.$el.find('footer').appendTo(this.$buttons); + } this.$sidebar = this.options.$sidebar || this.$el.find('.oe_form_sidebar'); if (!this.sidebar && this.options.$sidebar) { @@ -214,57 +216,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return $.when(); }, - extract_qweb_template: function(fvg) { - for (var i=0, ii=fvg.arch.children.length; i < ii; i++) { - var child = fvg.arch.children[i]; - if (child.tag === "templates") { - this.qweb = new QWeb2.Engine(); - this.qweb.add_template(instance.web.json_node_to_xml(child)); - if (!this.qweb.has_template('form')) { - throw new Error("No QWeb template found for form view"); - } - return true; - } - } - this.qweb = null; - return false; - }, - get_fvg_from_qweb: function(record) { - var view = this.qweb.render('form', this.get_qweb_context(record)); - var fvg = _.clone(this.fields_view); - fvg.arch = instance.web.xml_to_json(instance.web.str_to_xml(view).firstChild); - return fvg; - }, - get_qweb_context: function(record) { - var self = this, - new_record = {}; - _.each(record, function(value_, name) { - var r = _.clone(self.fields_view.fields[name] || {}); - if ((r.type === 'date' || r.type === 'datetime') && value_) { - r.raw_value = instance.web.auto_str_to_date(value_); - } else { - r.raw_value = value_; - } - r.value = instance.web.format_value(value_, r); - new_record[name] = r; - }); - return { - record : new_record, - new_record : !record.id - }; - }, - kill_current_form: function() { - _.each(this.getChildren(), function(el) { - el.destroy(); - }); - this.fields = {}; - this.fields_order = []; - this.default_focus_field = null; - this.default_focus_button = null; - this.translatable_fields = []; - this.$el.find('.oe_form_container').empty(); - }, - widgetFocused: function() { // Clear click flag if used to focus a widget this.__clicked_inside = false; @@ -368,13 +319,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this._actualize_mode(); this.set({ 'title' : record.id ? record.display_name : "New" }); - if (this.qweb) { - this.kill_current_form(); - this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record)); - var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container'); - this.rendering_engine.render_to($dest); - } - _(this.fields).each(function (field, f) { field._dirty_flag = false; field._inhibit_on_change_flag = true; @@ -395,6 +339,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }); } self.on_form_changed(); + self.rendering_engine.init_fields(); self.is_initialized.resolve(); self.do_update_pager(record.id == null); if (self.sidebar) { @@ -431,7 +376,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM do_notify_change: function() { this.$el.add(this.$buttons).addClass('oe_form_dirty'); }, - on_pager_action: function(action) { + execute_pager_action: function(action) { if (this.can_be_discarded()) { switch (action) { case 'first': @@ -448,6 +393,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM break; } this.reload(); + this.trigger('pager_action_executed'); } }, init_pager: function() { @@ -464,7 +410,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM } this.$pager.on('click','a[data-pager-action]',function() { var action = $(this).data('pager-action'); - self.on_pager_action(action); + self.execute_pager_action(action); }); this.do_update_pager(); }, @@ -737,7 +683,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM } for (var i = 0; i < fields_order.length; i += 1) { var field = this.fields[fields_order[i]]; - if (!field.get('effective_invisible') && !field.get('effective_readonly')) { + if (!field.get('effective_invisible') && !field.get('effective_readonly') && field.$label) { if (field.focus() !== false) { break; } @@ -747,7 +693,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }, on_button_save: function() { var self = this; - return this.do_save().then(function(result) { + return this.save().then(function(result) { + self.trigger("save", result); self.to_view_mode(); }); }, @@ -784,7 +731,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM var def = $.Deferred(); $.when(this.has_been_loaded).then(function() { self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { - return self.record_created({ result : new_id }); + return self.record_created(new_id); }).then(function() { return self.to_edit_mode(); }).then(function() { @@ -799,7 +746,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM $.when(this.has_been_loaded).then(function() { if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) { self.dataset.unlink([self.datarecord.id]).then(function() { - self.on_pager_action('next'); + self.execute_pager_action('next'); def.resolve(); }); } else { @@ -824,11 +771,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM * record or saving an existing one depending on whether the record * already has an id property. * - * @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new + * @param {Boolean} [prepend_on_create=false] if ``save`` creates a new * record, should that record be inserted at the start of the dataset (by * default, records are added at the end) */ - do_save: function(prepend_on_create) { + save: function(prepend_on_create) { var self = this; return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() { try { @@ -843,7 +790,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if (!first_invalid_field) { first_invalid_field = f; } - } else if (f.name !== 'id' && (!self.datarecord.id || (!f.get("readonly") && f._dirty_flag))) { + } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) { // Special case 'id' field, do not save this field // on 'create' : save all non readonly fields // on 'edit' : save non readonly modified fields @@ -956,7 +903,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM var self = this; return this.reload_mutex.exec(function() { if (self.dataset.index == null) { - self.do_prev_view(); + self.trigger("previous_view"); return $.Deferred().reject().promise(); } if (self.dataset.index == null || self.dataset.index < 0) { @@ -1000,7 +947,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }, recursive_save: function() { var self = this; - return $.when(this.do_save()).pipe(function(res) { + return $.when(this.save()).pipe(function(res) { if (self.dataset.parent_view) return self.dataset.parent_view.recursive_save(); }); @@ -1031,10 +978,20 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return true; }, sidebar_context: function () { - return this.do_save().pipe(_.bind(function() {return this.get_fields_values();}, this)); + return this.save().pipe(_.bind(function() {return this.get_fields_values();}, this)); }, open_defaults_dialog: function () { var self = this; + var display = function (field, value) { + if (field instanceof instance.web.form.FieldSelection) { + return _(field.values).find(function (option) { + return option[0] === value; + })[1]; + } else if (field instanceof instance.web.form.FieldMany2One) { + return field.get_displayed(); + } + return value; + } var fields = _.chain(this.fields) .map(function (field, name) { var value = field.get_value(); @@ -1048,29 +1005,28 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM || field.field.type === 'binary') { return false; } - var displayed = value; - if (field instanceof instance.web.form.FieldSelection) { - displayed = _(field.values).find(function (option) { - return option[0] === value; - })[1]; - } else if (field instanceof instance.web.form.FieldMany2One) { - displayed = field.get_displayed(); - } return { name: name, string: field.string, value: value, - displayed: displayed, - // convert undefined to false - change_default: !!field.field.change_default + displayed: display(field, value), } }) .compact() .sortBy(function (field) { return field.string; }) .value(); - var conditions = _.chain(fields) - .filter(function (field) { return field.change_default; }) + var conditions = _.chain(self.fields) + .filter(function (field) { return field.field.change_default; }) + .map(function (field, name) { + var value = field.get_value(); + return { + name: name, + string: field.string, + value: value, + displayed: display(field, value), + } + }) .value(); var d = new instance.web.Dialog(this, { @@ -1096,7 +1052,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM field_to_set, self.fields[field_to_set].get_value(), all_users, - false, + true, condition || false ]).then(function () { d.close(); }); }} @@ -1226,7 +1182,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt this.$form.appendTo(this.$target); - var ws = _.map(this.fields_to_init, function($elem) { + this.to_replace = []; + + _.each(this.fields_to_init, function($elem) { var name = $elem.attr("name"); if (!self.fvg.fields[name]) { throw new Error("Field '" + name + "' specified in view could not be found."); @@ -1242,24 +1200,28 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt } self.alter_field(w); self.view.register_field(w, $elem.attr("name")); - return [w, $elem]; - }); - _.each(ws, function(w) { - w[0].replace(w[1]); + self.to_replace.push([w, $elem]); }); _.each(this.tags_to_init, function($elem) { var tag_name = $elem[0].tagName.toLowerCase(); var obj = self.tags_registry.get_object(tag_name); var w = new (obj)(self.view, instance.web.xml_to_json($elem[0])); - w.replace($elem); + self.to_replace.push([w, $elem]); }); _.each(this.widgets_to_init, function($elem) { var widget_type = $elem.attr("type"); var obj = self.widgets_registry.get_object(widget_type); var w = new (obj)(self.view, instance.web.xml_to_json($elem[0])); - w.replace($elem); + self.to_replace.push([w, $elem]); }); - // TODO: return a deferred + }, + init_fields: function() { + var defs = []; + _.each(this.to_replace, function(el) { + defs.push(el[0].replace(el[1])); + }); + this.to_replace = []; + return $.when.apply($, defs); }, render_element: function(template /* dictionaries */) { var dicts = [].slice.call(arguments).slice(1); @@ -1660,6 +1622,8 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({ }); instance.web.form.compute_domain = function(expr, fields) { + if (! (expr instanceof Array)) + return !! expr; var stack = []; for (var i = expr.length - 1; i >= 0; i--) { var ex = expr[i]; @@ -2006,12 +1970,9 @@ instance.web.form.FieldInterface = { /** * Called by the form view to indicate the value of the field. * - * set_value() may return an object that can be passed to $.when() that represents the moment when - * the field has finished all operations necessary before the user can effectively use the widget. - * * Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation, - * regardless of any asynchronous operation currently running and the status of any promise that a - * previous call to set_value() could have returned. + * regardless of any asynchronous operation currently running. Calls to set_value() can and will also occur + * before the widget is inserted into the DOM. * * set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the * osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must @@ -2087,11 +2048,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w this.field = this.field_manager.get_field_desc(this.name); this.widget = this.node.attrs.widget; this.string = this.node.attrs.string || this.field.string || this.name; - try { - this.options = JSON.parse(this.node.attrs.options || '{}'); - } catch (e) { - throw new Error(_.str.sprintf(_t("Widget options for field '%s' are not valid JSON."), this.name)); - } + this.options = instance.web.py_eval(this.node.attrs.options || '{}'); this.set({'value': false}); this.on("change:value", this, function() { @@ -2125,6 +2082,14 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w this.field_manager.on("change:display_invalid_fields", this, this._check_css_flags); this._check_css_flags(); }, + start: function() { + var tmp = this._super(); + this.on("change:value", this, function() { + if (! this.no_rerender) + this.render_value(); + }); + this.render_value(); + }, /** * Private. Do not use. */ @@ -2137,6 +2102,20 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w get_value: function() { return this.get('value'); }, + /** + Utility method that all implementations should use to change the + value without triggering a re-rendering. + */ + internal_set_value: function(value_) { + var tmp = this.no_render; + this.no_rerender = true; + this.set({'value': value_}); + this.no_rerender = tmp; + }, + /** + This method is called each time the value is modified. + */ + render_value: function() {}, is_valid: function() { return this.is_syntax_valid() && !(this.get('required') && this.is_false()); }, @@ -2181,11 +2160,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w */ instance.web.form.ReinitializeWidgetMixin = { /** - * Default implementation of start(), use it or call explicitly initialize_field(). + * Default implementation of, you should not override it, use initialize_field() instead. */ start: function() { - this._super(); this.initialize_field(); + this._super(); }, initialize_field: function() { this.on("change:effective_readonly", this, this.reinitialize); @@ -2212,18 +2191,10 @@ instance.web.form.ReinitializeWidgetMixin = { * switch. */ instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.ReinitializeWidgetMixin, { - initialize_field: function() { - instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this); - this.render_value(); - }, reinitialize: function() { instance.web.form.ReinitializeWidgetMixin.reinitialize.call(this); this.render_value(); }, - /** - * Called to render the value. Should also be explicitly called at the end of a set_value(). - */ - render_value: function() {}, }); instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, { @@ -2237,14 +2208,10 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we var self = this; var $input = this.$el.find('input'); $input.change(function() { - self.set({'value': self.parse_value($input.val())}); + self.internal_set_value(self.parse_value($input.val())); }); this.setupFocus($input); }, - set_value: function(value_) { - this._super(value_); - this.render_value(); - }, render_value: function() { var show_value = this.format_value(this.get('value'), ''); if (!this.get("effective_readonly")) { @@ -2257,7 +2224,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we } }, is_syntax_valid: function() { - if (!this.get("effective_readonly")) { + if (!this.get("effective_readonly") && this.$("input").size() > 0) { try { var value_ = this.parse_value(this.$el.find('input').val(), ''); return true; @@ -2348,7 +2315,7 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ widget_class: 'oe_form_field_float', init: function (field_manager, node) { this._super(field_manager, node); - this.set({'value': 0}); + this.internal_set_value(0); if (this.node.attrs.digits) { this.digits = this.node.attrs.digits; } else { @@ -2379,7 +2346,10 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({ var self = this; this.$input = this.$el.find('input.oe_datepicker_master'); this.$input_picker = this.$el.find('input.oe_datepicker_container'); - this.$input.change(this.on_change); + this.$input.change(function(){ + self.change_datetime(); + }); + this.picker({ onClose: this.on_picker_select, onSelect: this.on_picker_select, @@ -2447,9 +2417,10 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({ format_client: function(v) { return instance.web.format_value(v, {"widget": this.type_of_date}); }, - on_change: function() { + change_datetime: function() { if (this.is_valid_()) { this.set_value_from_ui_(); + this.trigger("datetime_changed"); } } }); @@ -2473,17 +2444,13 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc initialize_content: function() { if (!this.get("effective_readonly")) { this.datewidget = this.build_widget(); - this.datewidget.on_change.add_last(_.bind(function() { - this.set({'value': this.datewidget.get_value()}); + this.datewidget.on('datetime_changed', this, _.bind(function() { + this.internal_set_value(this.datewidget.get_value()); }, this)); this.datewidget.appendTo(this.$el); this.setupFocus(this.datewidget.$input); } }, - set_value: function(value_) { - this._super(value_); - this.render_value(); - }, render_value: function() { if (!this.get("effective_readonly")) { this.datewidget.set_value(this.get('value')); @@ -2492,7 +2459,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instanc } }, is_syntax_valid: function() { - if (!this.get("effective_readonly")) { + if (!this.get("effective_readonly") && this.datewidget) { return this.datewidget.is_valid_(); } return true; @@ -2525,7 +2492,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we this.default_height = this.$textarea.css('height'); if (!this.get("effective_readonly")) { this.$textarea.change(_.bind(function() { - self.set({'value': instance.web.parse_value(self.$textarea.val(), self)}); + self.internal_set_value(instance.web.parse_value(self.$textarea.val(), self)); }, this)); } else { this.$textarea.attr('disabled', 'disabled'); @@ -2537,12 +2504,8 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we }); this.setupFocus(this.$textarea); }, - set_value: function(value_) { - this._super(value_); - this.render_value(); - $(window).resize(); - }, render_value: function() { + $(window).resize(); var show_value = instance.web.format_value(this.get('value'), this, ''); if (show_value === '') { this.$textarea.css('height', parseInt(this.default_height)+"px"); @@ -2551,7 +2514,7 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we this.$textarea.autosize(); }, is_syntax_valid: function() { - if (!this.get("effective_readonly")) { + if (!this.get("effective_readonly") && this.$textarea) { try { var value_ = instance.web.parse_value(this.$textarea.val(), this, ''); return true; @@ -2602,15 +2565,11 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc this.$cleditor.change(function() { if (! self._updating_editor) { self.$cleditor.updateTextArea(); - self.set({'value': self.$textarea.val()}); + self.internal_set_value(self.$textarea.val()); } }); } }, - set_value: function(value_) { - this._super.apply(this, arguments); - this.render_value(); - }, render_value: function() { if (! this.get("effective_readonly")) { this.$textarea.val(this.get('value') || ''); @@ -2627,21 +2586,20 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({ template: 'FieldBoolean', start: function() { var self = this; - this._super.apply(this, arguments); this.$checkbox = $("input", this.$el); this.setupFocus(this.$checkbox); this.$el.click(_.bind(function() { - this.set({'value': this.$checkbox.is(':checked')}); + this.internal_set_value(this.$checkbox.is(':checked')); }, this)); var check_readonly = function() { self.$checkbox.prop('disabled', self.get("effective_readonly")); }; this.on("change:effective_readonly", this, check_readonly); check_readonly.call(this); - }, - set_value: function(value_) { this._super.apply(this, arguments); - this.$checkbox[0].checked = value_; + }, + render_value: function() { + this.$checkbox[0].checked = this.get('value'); }, focus: function() { this.$checkbox.focus(); @@ -2657,9 +2615,8 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({ disabled: this.get("effective_readonly") }); }, - set_value: function(value_) { - this._super.apply(this, arguments); - var show_value = Number(value_); + render_value: function() { + var show_value = Number(this.get('value')); if (isNaN(show_value)) { show_value = 0; } @@ -2697,7 +2654,7 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan var ischanging = false; var $select = this.$el.find('select') .change(_.bind(function() { - this.set({'value': this.values[this.$el.find('select')[0].selectedIndex][0]}); + this.internal_set_value(this.values[this.$el.find('select')[0].selectedIndex][0]); }, this)) .change(function () { ischanging = true; }) .click(function () { ischanging = false; }) @@ -2712,7 +2669,6 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan value_ = value_ === null ? false : value_; value_ = value_ instanceof Array ? value_[0] : value_; this._super(value_); - this.render_value(); }, render_value: function() { if (!this.get("effective_readonly")) { @@ -2728,13 +2684,6 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan this.$el.text(option ? option[1] : this.values[0][1]); } }, - is_syntax_valid: function() { - if (this.get("effective_readonly")) { - return true; - } - var value_ = this.values[this.$el.find('select')[0].selectedIndex]; - return !! value_; - }, focus: function() { this.$el.find('select:first').focus(); } @@ -2877,7 +2826,7 @@ instance.web.form.CompletionFieldMixin = { self.build_domain(), new instance.web.CompoundContext(self.build_context(), context || {}) ); - pop.on_select_elements.add(function(element_ids) { + pop.on("elements_selected", self, function(element_ids) { self.add_id(element_ids[0]); self.focus(); }); @@ -2932,26 +2881,27 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc this.display_value = {}; this.last_search = []; this.floating = false; - this.inhibit_on_change = false; this.current_display = null; + this.is_started = false; }, - start: function() { - this._super(); - instance.web.form.ReinitializeFieldMixin.start.call(this); - this.on("change:value", this, function() { - this.floating = false; + reinit_value: function(val) { + this.internal_set_value(val); + this.floating = false; + if (this.is_started) this.render_value(); - }); + }, + initialize_field: function() { + this.is_started = true; instance.web.bus.on('click', this, function() { if (!this.get("effective_readonly") && this.$input && this.$input.autocomplete('widget').is(':visible')) { this.$input.autocomplete("close"); } }); + instance.web.form.ReinitializeFieldMixin.initialize_field.call(this); }, initialize_content: function() { if (!this.get("effective_readonly")) this.render_editable(); - this.render_value(); }, init_error_displayer: function() { // nothing @@ -3002,7 +2952,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc if (self.current_display !== self.$input.val()) { self.current_display = self.$input.val(); if (self.$input.val() === "") { - self.set({value: false}); + self.internal_set_value(false); self.floating = false; } else { self.floating = true; @@ -3034,15 +2984,14 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc if (self.last_search[0][0] != self.get("value")) { self.display_value = {}; self.display_value["" + self.last_search[0][0]] = self.last_search[0][1]; - self.set({value: self.last_search[0][0]}); + self.reinit_value(self.last_search[0][0]); } else { used = true; self.render_value(); } } else { used = true; - self.set({value: false}); - self.render_value(); + self.reinit_value(false); } self.floating = false; } @@ -3097,7 +3046,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc if (item.id) { self.display_value = {}; self.display_value["" + item.id] = item.name; - self.set({value: item.id}); + self.reinit_value(item.id); } else if (item.action) { item.action(); // Cancel widget blurring, to avoid form blur event @@ -3188,16 +3137,14 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc value_ = value_[0]; } value_ = value_ || false; - this.inhibit_on_change = true; - this._super(value_); - this.inhibit_on_change = false; + this.reinit_value(value_); }, get_displayed: function() { return this.display_value["" + this.get("value")]; }, add_id: function(id) { this.display_value = {}; - this.set({value: id}); + this.reinit_value(id); }, is_false: function() { return ! this.get("value"); @@ -3270,9 +3217,18 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ lazy_build_o2m_kanban_view(); this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; - this.is_setted = $.Deferred(); this.form_last_update = $.Deferred(); this.init_form_last_update = this.form_last_update; + this.is_started = false; + this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation); + this.dataset.o2m = this; + this.dataset.parent_view = this.view; + this.dataset.child_name = this.name; + var self = this; + this.dataset.on('dataset_changed', this, function() { + self.trigger_on_change(); + }); + this.set_value([]); }, start: function() { this._super.apply(this, arguments); @@ -3280,17 +3236,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ var self = this; - this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation); - this.dataset.o2m = this; - this.dataset.parent_view = this.view; - this.dataset.child_name = this.name; - this.dataset.on_change.add_last(function() { - self.trigger_on_change(); - }); - - this.is_setted.then(function() { - self.load_views(); - }); + self.load_views(); this.is_loaded.then(function() { self.on("change:effective_readonly", self, function() { self.is_loaded = self.is_loaded.pipe(function() { @@ -3301,6 +3247,8 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ }); }); }); + this.is_started = true; + this.reload_current_view(); }, trigger_on_change: function() { var tmp = this.doing_on_change; @@ -3372,7 +3320,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ var def = $.Deferred().then(function() { self.initial_is_loaded.resolve(); }); - this.viewmanager.on_controller_inited.add_last(function(view_type, controller) { + this.viewmanager.on("controller_inited", self, function(view_type, controller) { controller.o2m = self; if (view_type == "list") { if (self.get("effective_readonly")) { @@ -3387,9 +3335,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ controller.on("load_record", self, function(){ once.resolve(); }); - controller.on_pager_action.add_first(function() { - self.save_any_view(); - }); + controller.on('pager_action_executed',self,self.save_any_view); } else if (view_type == "graph") { self.reload_current_view() } @@ -3404,10 +3350,8 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ } }); }); - this.is_setted.then(function() { - $.async_when().then(function () { - self.viewmanager.appendTo(self.$el); - }); + $.async_when().then(function () { + self.viewmanager.appendTo(self.$el); }); return def; }, @@ -3487,9 +3431,12 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ if (this.dataset.index === null && this.dataset.ids.length > 0) { this.dataset.index = 0; } - self.is_setted.resolve(); this.trigger_on_change(); - return self.reload_current_view(); + if (this.is_started) { + return self.reload_current_view(); + } else { + return $.when(); + } }, get_value: function() { var self = this; @@ -3524,7 +3471,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ if (!view.is_initialized.isResolved()) { return false; } - var res = $.when(view.do_save()); + var res = $.when(view.save()); if (!res.isResolved() && !res.isRejected()) { console.warn("Asynchronous get_value() is not supported in form view."); } @@ -3581,7 +3528,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({ create_function: function(data) { return self.o2m.dataset.create(data).then(function(r) { self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r])); - self.o2m.dataset.on_change(); + self.o2m.dataset.trigger("dataset_changed", r); }); }, write_function: function(id, data, options) { @@ -3598,7 +3545,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({ form_view_options: {'not_interactible_on_create':true}, readonly: self.o2m.get("effective_readonly") }); - pop.on_select_elements.add_last(function() { + pop.on("elements_selected", self, function() { self.o2m.reload_current_view(); }); }, @@ -3676,7 +3623,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ create_function: function(data, callback, error_callback) { return self.o2m.dataset.create(data).then(function(r) { self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r])); - self.o2m.dataset.on_change(); + self.o2m.dataset.trigger("dataset_changed", r); }).then(callback, error_callback); }, read_function: function() { @@ -3689,7 +3636,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ self.o2m.build_domain(), self.o2m.build_context() ); - pop.on_select_elements.add_last(function() { + pop.on("elements_selected", self, function() { self.o2m.reload_current_view(); }); } @@ -3725,7 +3672,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ var self = this; this.ensure_saved().pipe(function () { if (parent_form) - return parent_form.do_save(); + return parent_form.save(); else return $.when(); }).then(function () { @@ -3864,7 +3811,7 @@ instance.web.form.One2ManyFormView = instance.web.FormView.extend({ this._super(data); var self = this; this.$buttons.find('button.oe_form_button_create').click(function() { - self.do_save().then(self.on_button_new); + self.save().then(self.on_button_new); }); }, do_notify_change: function() { @@ -3892,11 +3839,6 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in this._display_orderer = new instance.web.DropMisordered(); this._drop_shown = false; }, - start: function() { - this._super(); - instance.web.form.ReinitializeFieldMixin.start.call(this); - this.on("change:value", this, this.render_value); - }, initialize_content: function() { if (this.get("effective_readonly")) return; @@ -4018,36 +3960,33 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ disable_utility_classes: true, init: function(field_manager, node) { this._super(field_manager, node); - this.set({"value": []}); this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; - this.is_setted = $.Deferred(); + this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); + this.dataset.m2m = this; + var self = this; + this.dataset.on('unlink', self, function(ids) { + self.dataset_changed(); + }); + this.set_value([]); }, start: function() { - this._super.apply(this, arguments); this.$el.addClass('oe_form_field oe_form_field_many2many'); var self = this; - this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); - this.dataset.m2m = this; - this.dataset.on('unlink', self, function(ids) { - self.dataset_changed(); - }); - - this.is_setted.then(function() { - self.load_view(); - }); + self.load_view(); this.is_loaded.then(function() { self.on("change:effective_readonly", self, function() { self.is_loaded = self.is_loaded.pipe(function() { self.list_view.destroy(); return $.when(self.load_view()).then(function() { - self.reload_content(); + self.render_value(); }); }); }); }); + this._super.apply(this, arguments); }, set_value: function(value_) { value_ = value_ || []; @@ -4055,17 +3994,12 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ value_ = value_[0][2]; } this._super(value_); - this.dataset.set_ids(value_); - var self = this; - self.reload_content(); - this.is_setted.resolve(); }, get_value: function() { return [commands.replace_with(this.get('value'))]; }, - is_false: function () { - return _(this.dataset.ids).isEmpty(); + return _(this.get("value")).isEmpty(); }, load_view: function() { var self = this; @@ -4092,14 +4026,15 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ }); return loaded; }, - reload_content: function() { + render_value: function() { var self = this; + this.dataset.set_ids(this.get("value")); this.is_loaded = this.is_loaded.pipe(function() { return self.list_view.reload_content(); }); }, dataset_changed: function() { - this.set({'value': this.dataset.ids}); + this.internal_set_value(this.dataset.ids); }, }); @@ -4126,7 +4061,7 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in this.m2m_field.build_context() ); var self = this; - pop.on_select_elements.add(function(element_ids) { + pop.on("elements_selected", self, function(element_ids) { _.each(element_ids, function(one_id) { if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) { self.dataset.set_ids([].concat(self.dataset.ids, [one_id])); @@ -4155,28 +4090,26 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( m2m_kanban_lazy_init(); this.is_loaded = $.Deferred(); this.initial_is_loaded = this.is_loaded; - this.is_setted = $.Deferred(); + + var self = this; + this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); + this.dataset.m2m = this; + this.dataset.on('unlink', self, function(ids) { + self.dataset_changed(); + }); }, start: function() { this._super.apply(this, arguments); var self = this; - this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); - this.dataset.m2m = this; - this.dataset.on('unlink', self, function(ids) { - self.dataset_changed(); - }); - - this.is_setted.then(function() { - self.load_view(); - }); + self.load_view(); this.is_loaded.then(function() { self.on("change:effective_readonly", self, function() { self.is_loaded = self.is_loaded.pipe(function() { self.kanban_view.destroy(); return $.when(self.load_view()).then(function() { - self.reload_content(); + self.render_value(); }); }); }); @@ -4188,10 +4121,6 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( value_ = value_[0][2]; } this._super(value_); - this.dataset.set_ids(value_); - var self = this; - self.reload_content(); - this.is_setted.resolve(); }, load_view: function() { var self = this; @@ -4218,8 +4147,9 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( }); return loaded; }, - reload_content: function() { + render_value: function() { var self = this; + this.dataset.set_ids(this.get("value")); this.is_loaded = this.is_loaded.pipe(function() { return self.kanban_view.do_search(self.build_domain(), self.dataset.get_context(), []); }); @@ -4241,12 +4171,12 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( new instance.web.CompoundDomain(this.build_domain(), ["!", ["id", "in", this.dataset.ids]]), this.build_context() ); - pop.on_select_elements.add(function(element_ids) { + pop.on("elements_selected", self, function(element_ids) { _.each(element_ids, function(one_id) { if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) { self.dataset.set_ids([].concat(self.dataset.ids, [one_id])); self.dataset_changed(); - self.reload_content(); + self.render_value(); } }); }); @@ -4257,7 +4187,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( title: _t("Open: ") + self.string, write_function: function(id, data, options) { return self.dataset.write(id, data, {}).then(function() { - self.reload_content(); + self.render_value(); }); }, alternative_form_view: self.field.views ? self.field.views["form"] : undefined, @@ -4407,9 +4337,8 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ self.check_exit(true); }, title: this.options.title || "", - buttons: [{text:"tmp"}], }, this.$el).open(); - this.$buttonpane = dialog.$el.dialog("widget").find(".ui-dialog-buttonpane").html(""); + this.$buttonpane = dialog.$buttons; this.start(); }, setup_form_view: function() { @@ -4440,7 +4369,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ })); var $snbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save-new"); $snbutton.click(function() { - $.when(self.view_form.do_save()).then(function() { + $.when(self.view_form.save()).then(function() { self.view_form.reload_mutex.exec(function() { self.view_form.on_button_new(); }); @@ -4448,7 +4377,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ }); var $sbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save"); $sbutton.click(function() { - $.when(self.view_form.do_save()).then(function() { + $.when(self.view_form.save()).then(function() { self.view_form.reload_mutex.exec(function() { self.check_exit(); }); @@ -4461,11 +4390,12 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({ self.view_form.do_show(); }); }, - on_select_elements: function(element_ids) { + select_elements: function(element_ids) { + this.trigger("elements_selected", element_ids); }, check_exit: function(no_destroy) { if (this.created_elements.length > 0) { - this.on_select_elements(this.created_elements); + this.select_elements(this.created_elements); this.created_elements = []; } this.destroy(); @@ -4576,7 +4506,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend }); var $sbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-select"); $sbutton.click(function() { - self.on_select_elements(self.selected_ids); + self.select_elements(self.selected_ids); self.destroy(); }); }); @@ -4618,7 +4548,7 @@ instance.web.form.SelectCreateListView = instance.web.ListView.extend({ this.popup.new_object(); }, select_record: function(index) { - this.popup.on_select_elements([this.dataset.ids[index]]); + this.popup.select_elements([this.dataset.ids[index]]); this.popup.destroy(); }, do_select: function(ids, records) { @@ -4646,6 +4576,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan destroy_content: function() { if (this.fm) { this.fm.destroy(); + this.fm = undefined; } }, initialize_content: function() { @@ -4667,9 +4598,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan modifiers: JSON.stringify({readonly: this.get('effective_readonly')}), }}); this.selection.on("change:value", this, this.on_selection_changed); - this.selection.setElement(this.$(".oe_form_view_reference_selection")); - this.selection.renderElement(); - this.selection.start(); + this.selection.appendTo(this.$(".oe_form_view_reference_selection")); this.selection .on('focused', null, function () {self.trigger('focused')}) .on('blurred', null, function () {self.trigger('blurred')}); @@ -4679,9 +4608,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan modifiers: JSON.stringify({readonly: this.get('effective_readonly')}), }}); this.m2o.on("change:value", this, this.data_changed); - this.m2o.setElement(this.$(".oe_form_view_reference_m2o")); - this.m2o.renderElement(); - this.m2o.start(); + this.m2o.appendTo(this.$(".oe_form_view_reference_m2o")); this.m2o .on('focused', null, function () {self.trigger('focused')}) .on('blurred', null, function () {self.trigger('blurred')}); @@ -4689,10 +4616,6 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan is_false: function() { return typeof(this.get_value()) !== 'string'; }, - set_value: function(value_) { - this._super(value_); - this.render_value(); - }, render_value: function() { this.reference_ready = false; var vals = [], sel_val, m2o_val; @@ -4712,9 +4635,9 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan var model = this.selection.get_value(), id = this.m2o.get_value(); if (typeof(model) === 'string' && typeof(id) === 'number') { - this.set({'value': model + ',' + id}); + this.internal_set_value(model + ',' + id); } else { - this.set({'value': false}); + this.internal_set_value(false); } }, }); @@ -4813,7 +4736,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance. on_clear: function() { if (this.get('value') !== false) { this.binary_value = false; - this.set({'value': false}); + this.internal_set_value(false); } return false; } @@ -4833,10 +4756,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ }); } }, - set_value: function(value_) { - this._super.apply(this, arguments); - this.render_value(); - }, render_value: function() { if (!this.get("effective_readonly")) { var show_value; @@ -4858,7 +4777,7 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ }, on_file_uploaded_and_valid: function(size, name, content_type, file_base64) { this.binary_value = true; - this.set({'value': file_base64}); + this.internal_set_value(file_base64); var show_value = name + " (" + this.human_filesize(size) + ")"; this.$el.find('input').eq(0).val(show_value); this.set_filename(name); @@ -4872,10 +4791,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ template: 'FieldBinaryImage', - set_value: function(value_) { - this._super.apply(this, arguments); - this.render_value(); - }, render_value: function() { var self = this; var url; @@ -4908,7 +4823,7 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ this._super.apply(this, arguments); }, on_file_uploaded_and_valid: function(size, name, content_type, file_base64) { - this.set({'value': file_base64}); + this.internal_set_value(file_base64); this.binary_value = true; this.render_value(); this.set_filename(name); @@ -4926,38 +4841,34 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ this._super(field_manager, node); this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false; this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false; - this.selected_value = null; + this.set({value: false}); }, start: function() { - this._super(); - // backward compatibility - this.loaded = new $.Deferred(); if (this.options.clickable) { this.$el.on('click','li',this.on_click_stage); } - // TODO move the following into css :after if (this.$el.parent().is('header')) { this.$el.after('
'); } + this._super(); }, set_value: function(value_) { - var self = this; - this._super(value_); - // find selected value: - // - many2one: [2, "New"] -> 2 - // - selection: new -> new - if (this.field.type == "many2one") { - this.selected_value = value_[0]; - } else { - this.selected_value = value_; + if (value_ instanceof Array) { + value_ = value_[0]; } - // trick to be sure all values are loaded in the form, therefore - // enabling the evaluation of dynamic domains - self.selection = []; - $.async_when().then(function() { - self.get_selection(); + this._super(value_); + }, + render_value: function() { + var self = this; + self.get_selection().then(function() { + var content = QWeb.render("FieldStatus.content", {widget: self}); + self.$el.html(content); + var colors = JSON.parse((self.node.attrs || {}).statusbar_colors || "{}"); + var color = colors[self.get('value')]; + if (color) { + self.$("oe_active").css("color", color); + } }); - return this.loaded; }, /** Get the selection and render it * selection: [[identifier, value_to_display], ...] @@ -4966,53 +4877,37 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({ */ get_selection: function() { var self = this; + self.selection = []; if (this.field.type == "many2one") { var domain = []; if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) { - domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]); + domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.get('value')]]); } var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain); - ds.read_slice(['name'], {}).done( function (records) { + return ds.read_slice(['name'], {}).pipe(function (records) { for(var i = 0; i < records.length; i++) { self.selection.push([records[i].id, records[i].name]); } - self.render_elements(); - self.loaded.resolve(); }); } else { - this.loaded.resolve(); // For field type selection filter values according to // statusbar_visible attribute of the field. For example: // statusbar_visible="draft,open". var selection = this.field.selection; - for(var i=0; i< selection.length; i++) { + for(var i=0; i < selection.length; i++) { var key = selection[i][0]; - if(key == this.selected_value || !this.options.visible || this.options.visible.indexOf(key) != -1) { + if(key == this.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) { this.selection.push(selection[i]); } } - this.render_elements(); - } - }, - /** Renders the widget. This function also checks for statusbar_colors='{"pending": "blue"}' - * attribute in the widget. This allows to set a given color to a given - * state (given by the key of (key, label)). - */ - render_elements: function () { - var self = this; - var content = instance.web.qweb.render("FieldStatus.content", {widget: this}); - this.$el.html(content); - var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}"); - var color = colors[this.selected_value]; - if (color) { - this.$("oe_active").css("color", color); + return $.when(); } }, on_click_stage: function (ev) { var self = this; var $li = $(ev.currentTarget); var val = parseInt($li.data("id")); - if (val != self.selected_value) { + if (val != self.get('value')) { this.view.recursive_save().then(function() { var change = {}; change[self.name] = val; diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 7457ca5ef67..a6aa637f655 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -64,9 +64,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi this.set_groups(new (this.options.GroupsType)(this)); if (this.dataset instanceof instance.web.DataSetStatic) { - this.groups.datagroup = new instance.web.StaticDataGroup(this.dataset); + this.groups.datagroup = new StaticDataGroup(this.dataset); } else { - this.groups.datagroup = new instance.web.DataGroup( + this.groups.datagroup = new DataGroup( this, this.model, dataset.get_domain(), dataset.get_context()); @@ -399,14 +399,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi */ setup_columns: function (fields, grouped) { var registry = instance.web.list.columns; - var reorder = this.options.reorderable; this.columns.splice(0, this.columns.length); this.columns.push.apply(this.columns, _(this.fields_view.arch.children).map(function (field) { var id = field.attrs.name; - if(field.attrs.widget == 'handle' && !reorder){ - field.attrs.reorderable = reorder || true; - } return registry.for_(id, fields[id], field); })); if (grouped) { @@ -415,8 +411,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi } this.visible_columns = _.filter(this.columns, function (column) { - return column.invisible !== '1' && !column.reorderable; + return column.invisible !== '1'; }); + this.aggregate_columns = _(this.visible_columns).invoke('to_aggregate'); }, /** @@ -557,7 +554,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi */ do_search: function (domain, context, group_by) { this.page = 0; - this.groups.datagroup = new instance.web.DataGroup( + this.groups.datagroup = new DataGroup( this, this.model, domain, context, group_by); this.groups.datagroup.sort = this.dataset._sort; @@ -1153,7 +1150,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we passtrough_events: 'action deleted row_link', /** * Grouped display for the ListView. Handles basic DOM events and interacts - * with the :js:class:`~instance.web.DataGroup` bound to it. + * with the :js:class:`~DataGroup` bound to it. * * Provides events similar to those of * :js:class:`~instance.web.ListView.List` @@ -1558,6 +1555,60 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we } }); +var DataGroup = instance.web.CallbackEnabled.extend({ + init: function(parent, model, domain, context, group_by, level) { + this._super(parent, null); + this.model = new instance.web.Model(model, context, domain); + this.group_by = group_by; + this.context = context; + this.domain = domain; + + this.level = level || 0; + }, + list: function (fields, ifGroups, ifRecords) { + var self = this; + var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by); + $.when(query).then(function (querygroups) { + // leaf node + if (!querygroups) { + var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain()); + ds._sort = self.sort; + ifRecords(ds); + return; + } + // internal node + var child_datagroups = _(querygroups).map(function (group) { + var child_context = _.extend( + {}, self.model.context(), group.model.context()); + var child_dg = new DataGroup( + self, self.model.name, group.model.domain(), + child_context, group.model._context.group_by, + self.level + 1); + child_dg.sort = self.sort; + // copy querygroup properties + child_dg.__context = child_context; + child_dg.__domain = group.model.domain(); + child_dg.folded = group.get('folded'); + child_dg.grouped_on = group.get('grouped_on'); + child_dg.length = group.get('length'); + child_dg.value = group.get('value'); + child_dg.openable = group.get('has_children'); + child_dg.aggregates = group.get('aggregates'); + return child_dg; + }); + ifGroups(child_datagroups); + }); + } +}); +var StaticDataGroup = DataGroup.extend({ + init: function (dataset) { + this.dataset = dataset; + }, + list: function (fields, ifGroups, ifRecords) { + ifRecords(this.dataset); + } +}); + /** * @mixin Events */ diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index fe0f525fe3f..cd6b018ea0d 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -201,10 +201,7 @@ openerp.web.list_editable = function (instance) { }, function () { return self.editor.edit(item, function (field_name, field) { var cell = cells[field_name]; - if (!cell || field.get('effective_readonly')) { - // Readonly fields can just remain the list's, - // form's usually don't have backgrounds &al - field.set({invisible: true}); + if (!cell) { return; } @@ -400,6 +397,21 @@ openerp.web.list_editable = function (instance) { }, setup_events: function () { var self = this; + _.each(this.editor.form.fields, function(field, field_name) { + var field; + var setting = false; + var set_invisible = function() { + if (!setting && field.get("effective_readonly")) { + setting = true; + field.set({invisible: true}); + setting = false; + } + }; + field.on("change:effective_readonly", self, set_invisible); + field.on("change:invisible", self, set_invisible); + set_invisible(); + }); + this.editor.$el.on('keyup keydown', function (e) { if (!self.editor.is_editing()) { return; } var key = _($.ui.keyCode).chain() @@ -724,7 +736,7 @@ openerp.web.list_editable = function (instance) { save: function () { var self = this; return this.form - .do_save(this.delegate.prepends_on_create()) + .save(this.delegate.prepends_on_create()) .pipe(function (result) { var created = result.created && !self.record.id; if (created) { diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index f985e5f6dc8..45993db27bb 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -113,6 +113,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ var item = this.breadcrumbs[index]; item.show(subindex); this.inner_widget = item.widget; + this.inner_action = item.action; return true; }, clear_breadcrumbs: function() { @@ -131,6 +132,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ if (!dups.length) { if (this.getParent().has_uncommitted_changes()) { this.inner_widget = item.widget; + this.inner_action = item.action; this.breadcrumbs.splice(index, 0, item); return false; } else { @@ -139,7 +141,10 @@ instance.web.ActionManager = instance.web.Widget.extend({ } } var last_widget = this.breadcrumbs.slice(-1)[0]; - this.inner_widget = last_widget && last_widget.widget; + if (last_widget) { + this.inner_widget = last_widget.widget; + this.inner_action = last_widget.action; + } }, get_title: function() { var titles = []; @@ -164,6 +169,10 @@ instance.web.ActionManager = instance.web.Widget.extend({ state = state || {}; if (this.getParent() && this.getParent().do_push_state) { if (this.inner_action) { + if (this.inner_action._push_me === false) { + // this action has been explicitly marked as not pushable + return; + } state['title'] = this.inner_action.name; if(this.inner_action.type == 'ir.actions.act_window') { state['model'] = this.inner_action.res_model; @@ -172,7 +181,13 @@ instance.web.ActionManager = instance.web.Widget.extend({ state['action'] = this.inner_action.id; } else if (this.inner_action.type == 'ir.actions.client') { state['action'] = this.inner_action.tag; - //state = _.extend(this.inner_action.params || {}, state); + var params = {}; + _.each(this.inner_action.params, function(v, k) { + if(_.isString(v) || _.isNumber(v)) { + params[k] = v; + } + }); + state = _.extend(params || {}, state); } } if(!this.dialog) { @@ -224,14 +239,19 @@ instance.web.ActionManager = instance.web.Widget.extend({ } }); }, - do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { + do_action: function(action, options) { + options = _.defaults(options || {}, { + clear_breadcrumbs: false, + on_reverse_breadcrumb: function() {}, + on_close: function() {}, + }); if (_.isString(action) && instance.web.client_actions.contains(action)) { var action_client = { type: "ir.actions.client", tag: action }; - return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb); + return this.do_action(action_client, options); } else if (_.isNumber(action) || _.isString(action)) { var self = this; return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) { - return self.do_action(result, on_close, clear_breadcrumbs, on_reverse_breadcrumb); + return self.do_action(result, options); }); } if (!action.type) { @@ -253,7 +273,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ console.error("Action manager can't handle action of type " + action.type, action); return $.Deferred().reject(); } - return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb); + return this[type](action, options); }, null_action: function() { this.dialog_stop(); @@ -266,32 +286,36 @@ instance.web.ActionManager = instance.web.Widget.extend({ * @param {Function} executor.widget function used to fetch the widget instance * @param {String} executor.klass CSS class to add on the dialog root, if action.target=new * @param {Function} executor.post_process cleanup called after a widget has been added as inner_widget - * @param on_close - * @param clear_breadcrumbs + * @param {Object} options * @return {*} */ - ir_actions_common: function(executor, on_close, clear_breadcrumbs) { + ir_actions_common: function(executor, options) { if (this.inner_widget && executor.action.target !== 'new') { if (this.getParent().has_uncommitted_changes()) { return $.Deferred().reject(); - } else if (clear_breadcrumbs) { + } else if (options.clear_breadcrumbs) { this.clear_breadcrumbs(); } } var widget = executor.widget(); if (executor.action.target === 'new') { - if (this.dialog === null) { - // These buttons will be overwrited by