diff --git a/muk_utils/LICENSE b/muk_utils/LICENSE
index faf7bf4..153d416 100644
--- a/muk_utils/LICENSE
+++ b/muk_utils/LICENSE
@@ -1,619 +1,165 @@
- GNU AFFERO GENERAL PUBLIC LICENSE
- Version 3, 19 November 2007
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
- Copyright (C) 2007 Free Software Foundation, Inc.
+ 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
\ No newline at end of file
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser 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
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
diff --git a/muk_utils/README.rst b/muk_utils/README.rst
index 290b652..9cde5dd 100644
--- a/muk_utils/README.rst
+++ b/muk_utils/README.rst
@@ -1,113 +1,113 @@
-=========
-MuK Utils
-=========
-
-Technical module to provide some utility features and libraries that can be used
-in other applications. This module has no direct effect on the running system.
-
-Installation
-============
-
-To install this module, you need to:
-
-Download the module and add it to your Odoo addons folder. Afterward, log on to
-your Odoo server and go to the Apps menu. Trigger the debug mode and update the
-list by clicking on the "Update Apps List" link. Now install the module by
-clicking on the install button.
-
-Another way to install this module is via the package management for Python
-(`PyPI `_).
-
-To install our modules using the package manager make sure
-`odoo-autodiscover `_ is installed
-correctly. Then open a console and install the module by entering the following
-command:
-
-``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
-
-The module name consists of the Odoo version and the module name, where
-underscores are replaced by a dash.
-
-**Module:**
-
-``odoo-addon-``
-
-**Example:**
-
-``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils``
-
-Once the installation has been successfully completed, the app is already in the
-correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the
-debug mode and update the list by clicking on the "Update Apps List" link. Now
-install the module by clicking on the install button.
-
-The biggest advantage of this variant is that you can now also update the app
-using the "pip" command. To do this, enter the following command in your console:
-
-``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
-
-When the process is finished, restart your server and update the application in
-Odoo. The steps are the same as for the installation only the button has changed
-from "Install" to "Upgrade".
-
-You can also view available Apps directly in our `repository `_
-and find a more detailed installation guide on our `website `_.
-
-For modules licensed under OPL-1, you will receive access data when you purchase
-the module. If the modules were not purchased directly from
-`MuK IT `_ please contact our support (support@mukit.at)
-with a confirmation of purchase to receive the corresponding access data.
-
-Upgrade
-============
-
-To upgrade this module, you need to:
-
-Download the module and add it to your Odoo addons folder. Restart the server
-and log on to your Odoo server. Select the Apps menu and upgrade the module by
-clicking on the upgrade button.
-
-If you installed the module using the "pip" command, you can also update the
-module in the same way. Just type the following command into the console:
-
-``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
-
-When the process is finished, restart your server and update the application in
-Odoo, just like you would normally.
-
-Configuration
-=============
-
-No additional configuration is needed to use this module.
-
-Usage
-=============
-
-This module has no direct visible effect on the system. It provide utility features.
-
-Credits
-=======
-
-Contributors
-------------
-
-* Mathias Markl
-
-Images
-------------
-
-Some pictures are based on or inspired by the icon set of Font Awesome:
-
-* `Font Awesome `_
-
-Author & Maintainer
--------------------
-
-This module is maintained by the `MuK IT GmbH `_.
-
-MuK IT is an Austrian company specialized in customizing and extending Odoo.
-We develop custom solutions for your individual needs to help you focus on
-your strength and expertise to grow your business.
-
-If you want to get in touch please contact us via mail
-(sale@mukit.at) or visit our website (https://mukit.at).
+=========
+MuK Utils
+=========
+
+Technical module to provide some utility features and libraries that can be used
+in other applications. This module has no direct effect on the running system.
+
+Installation
+============
+
+To install this module, you need to:
+
+Download the module and add it to your Odoo addons folder. Afterward, log on to
+your Odoo server and go to the Apps menu. Trigger the debug mode and update the
+list by clicking on the "Update Apps List" link. Now install the module by
+clicking on the install button.
+
+Another way to install this module is via the package management for Python
+(`PyPI `_).
+
+To install our modules using the package manager make sure
+`odoo-autodiscover `_ is installed
+correctly. Then open a console and install the module by entering the following
+command:
+
+``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
+
+The module name consists of the Odoo version and the module name, where
+underscores are replaced by a dash.
+
+**Module:**
+
+``odoo-addon-``
+
+**Example:**
+
+``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils``
+
+Once the installation has been successfully completed, the app is already in the
+correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the
+debug mode and update the list by clicking on the "Update Apps List" link. Now
+install the module by clicking on the install button.
+
+The biggest advantage of this variant is that you can now also update the app
+using the "pip" command. To do this, enter the following command in your console:
+
+``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
+
+When the process is finished, restart your server and update the application in
+Odoo. The steps are the same as for the installation only the button has changed
+from "Install" to "Upgrade".
+
+You can also view available Apps directly in our `repository `_
+and find a more detailed installation guide on our `website `_.
+
+For modules licensed under OPL-1, you will receive access data when you purchase
+the module. If the modules were not purchased directly from
+`MuK IT `_ please contact our support (support@mukit.at)
+with a confirmation of purchase to receive the corresponding access data.
+
+Upgrade
+============
+
+To upgrade this module, you need to:
+
+Download the module and add it to your Odoo addons folder. Restart the server
+and log on to your Odoo server. Select the Apps menu and upgrade the module by
+clicking on the upgrade button.
+
+If you installed the module using the "pip" command, you can also update the
+module in the same way. Just type the following command into the console:
+
+``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
+
+When the process is finished, restart your server and update the application in
+Odoo, just like you would normally.
+
+Configuration
+=============
+
+No additional configuration is needed to use this module.
+
+Usage
+=============
+
+This module has no direct visible effect on the system. It provide utility features.
+
+Credits
+=======
+
+Contributors
+------------
+
+* Mathias Markl
+
+Images
+------------
+
+Some pictures are based on or inspired by the icon set of Font Awesome:
+
+* `Font Awesome `_
+
+Author & Maintainer
+-------------------
+
+This module is maintained by the `MuK IT GmbH `_.
+
+MuK IT is an Austrian company specialized in customizing and extending Odoo.
+We develop custom solutions for your individual needs to help you focus on
+your strength and expertise to grow your business.
+
+If you want to get in touch please contact us via mail
+(sale@mukit.at) or visit our website (https://mukit.at).
diff --git a/muk_utils/__init__.py b/muk_utils/__init__.py
index 5b5d62d..1124bf2 100644
--- a/muk_utils/__init__.py
+++ b/muk_utils/__init__.py
@@ -1,19 +1,22 @@
###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
diff --git a/muk_utils/__manifest__.py b/muk_utils/__manifest__.py
index d0a0b0b..2d52aad 100644
--- a/muk_utils/__manifest__.py
+++ b/muk_utils/__manifest__.py
@@ -1,27 +1,30 @@
###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
{
"name": "MuK Utils",
"summary": """Utility Features""",
- "version": '12.0.1.6.27',
+ "version": '12.0.2.0.0',
"category": 'Extra Tools',
- "license": "AGPL-3",
+ "license": "LGPL-3",
"author": "MuK IT",
"website": "https://www.mukit.at",
'live_test_url': 'https://mukit.at/r/SgN',
diff --git a/muk_utils/actions/ir_attachment.xml b/muk_utils/actions/ir_attachment.xml
index 85afea6..a051505 100644
--- a/muk_utils/actions/ir_attachment.xml
+++ b/muk_utils/actions/ir_attachment.xml
@@ -1,30 +1,35 @@
-
-
-
-
-
-
-
- Migrate
-
-
- code
- records.action_migrate()
-
-
+
+
+
+
+
+
+
+ Migrate
+
+
+ code
+ records.action_migrate()
+
+
\ No newline at end of file
diff --git a/muk_utils/doc/index.rst b/muk_utils/doc/index.rst
index 290b652..9cde5dd 100644
--- a/muk_utils/doc/index.rst
+++ b/muk_utils/doc/index.rst
@@ -1,113 +1,113 @@
-=========
-MuK Utils
-=========
-
-Technical module to provide some utility features and libraries that can be used
-in other applications. This module has no direct effect on the running system.
-
-Installation
-============
-
-To install this module, you need to:
-
-Download the module and add it to your Odoo addons folder. Afterward, log on to
-your Odoo server and go to the Apps menu. Trigger the debug mode and update the
-list by clicking on the "Update Apps List" link. Now install the module by
-clicking on the install button.
-
-Another way to install this module is via the package management for Python
-(`PyPI `_).
-
-To install our modules using the package manager make sure
-`odoo-autodiscover `_ is installed
-correctly. Then open a console and install the module by entering the following
-command:
-
-``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
-
-The module name consists of the Odoo version and the module name, where
-underscores are replaced by a dash.
-
-**Module:**
-
-``odoo-addon-``
-
-**Example:**
-
-``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils``
-
-Once the installation has been successfully completed, the app is already in the
-correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the
-debug mode and update the list by clicking on the "Update Apps List" link. Now
-install the module by clicking on the install button.
-
-The biggest advantage of this variant is that you can now also update the app
-using the "pip" command. To do this, enter the following command in your console:
-
-``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
-
-When the process is finished, restart your server and update the application in
-Odoo. The steps are the same as for the installation only the button has changed
-from "Install" to "Upgrade".
-
-You can also view available Apps directly in our `repository `_
-and find a more detailed installation guide on our `website `_.
-
-For modules licensed under OPL-1, you will receive access data when you purchase
-the module. If the modules were not purchased directly from
-`MuK IT `_ please contact our support (support@mukit.at)
-with a confirmation of purchase to receive the corresponding access data.
-
-Upgrade
-============
-
-To upgrade this module, you need to:
-
-Download the module and add it to your Odoo addons folder. Restart the server
-and log on to your Odoo server. Select the Apps menu and upgrade the module by
-clicking on the upgrade button.
-
-If you installed the module using the "pip" command, you can also update the
-module in the same way. Just type the following command into the console:
-
-``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
-
-When the process is finished, restart your server and update the application in
-Odoo, just like you would normally.
-
-Configuration
-=============
-
-No additional configuration is needed to use this module.
-
-Usage
-=============
-
-This module has no direct visible effect on the system. It provide utility features.
-
-Credits
-=======
-
-Contributors
-------------
-
-* Mathias Markl
-
-Images
-------------
-
-Some pictures are based on or inspired by the icon set of Font Awesome:
-
-* `Font Awesome `_
-
-Author & Maintainer
--------------------
-
-This module is maintained by the `MuK IT GmbH `_.
-
-MuK IT is an Austrian company specialized in customizing and extending Odoo.
-We develop custom solutions for your individual needs to help you focus on
-your strength and expertise to grow your business.
-
-If you want to get in touch please contact us via mail
-(sale@mukit.at) or visit our website (https://mukit.at).
+=========
+MuK Utils
+=========
+
+Technical module to provide some utility features and libraries that can be used
+in other applications. This module has no direct effect on the running system.
+
+Installation
+============
+
+To install this module, you need to:
+
+Download the module and add it to your Odoo addons folder. Afterward, log on to
+your Odoo server and go to the Apps menu. Trigger the debug mode and update the
+list by clicking on the "Update Apps List" link. Now install the module by
+clicking on the install button.
+
+Another way to install this module is via the package management for Python
+(`PyPI `_).
+
+To install our modules using the package manager make sure
+`odoo-autodiscover `_ is installed
+correctly. Then open a console and install the module by entering the following
+command:
+
+``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
+
+The module name consists of the Odoo version and the module name, where
+underscores are replaced by a dash.
+
+**Module:**
+
+``odoo-addon-``
+
+**Example:**
+
+``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils``
+
+Once the installation has been successfully completed, the app is already in the
+correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the
+debug mode and update the list by clicking on the "Update Apps List" link. Now
+install the module by clicking on the install button.
+
+The biggest advantage of this variant is that you can now also update the app
+using the "pip" command. To do this, enter the following command in your console:
+
+``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
+
+When the process is finished, restart your server and update the application in
+Odoo. The steps are the same as for the installation only the button has changed
+from "Install" to "Upgrade".
+
+You can also view available Apps directly in our `repository `_
+and find a more detailed installation guide on our `website `_.
+
+For modules licensed under OPL-1, you will receive access data when you purchase
+the module. If the modules were not purchased directly from
+`MuK IT `_ please contact our support (support@mukit.at)
+with a confirmation of purchase to receive the corresponding access data.
+
+Upgrade
+============
+
+To upgrade this module, you need to:
+
+Download the module and add it to your Odoo addons folder. Restart the server
+and log on to your Odoo server. Select the Apps menu and upgrade the module by
+clicking on the upgrade button.
+
+If you installed the module using the "pip" command, you can also update the
+module in the same way. Just type the following command into the console:
+
+``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple ``
+
+When the process is finished, restart your server and update the application in
+Odoo, just like you would normally.
+
+Configuration
+=============
+
+No additional configuration is needed to use this module.
+
+Usage
+=============
+
+This module has no direct visible effect on the system. It provide utility features.
+
+Credits
+=======
+
+Contributors
+------------
+
+* Mathias Markl
+
+Images
+------------
+
+Some pictures are based on or inspired by the icon set of Font Awesome:
+
+* `Font Awesome `_
+
+Author & Maintainer
+-------------------
+
+This module is maintained by the `MuK IT GmbH `_.
+
+MuK IT is an Austrian company specialized in customizing and extending Odoo.
+We develop custom solutions for your individual needs to help you focus on
+your strength and expertise to grow your business.
+
+If you want to get in touch please contact us via mail
+(sale@mukit.at) or visit our website (https://mukit.at).
diff --git a/muk_utils/i18n/ar.po b/muk_utils/i18n/ar.po
new file mode 100644
index 0000000..ba1adcd
--- /dev/null
+++ b/muk_utils/i18n/ar.po
@@ -0,0 +1,211 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * muk_utils
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-07-13 09:39+0000\n"
+"PO-Revision-Date: 2019-07-13 09:39+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Save this page before triggering the migration."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "All Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Field"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Model"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_attachment
+msgid "Attachment"
+msgstr "مرفق"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Attachment storage location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,help:muk_utils.field_res_config_settings__attachment_location
+msgid "Attachment storage location."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attachments"
+msgstr "المُرفقات"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_base
+msgid "Base"
+msgstr "الأساس"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__child_groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Child Groups"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_res_config_settings
+msgid "Config Settings"
+msgstr "ضبط الإعدادات"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__display_name
+msgid "Display Name"
+msgstr "اسم العرض"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__explicit_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Explicit Users"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Field Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Force Storage Migration"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Group"
+msgstr "المجموعة"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_groups
+msgid "Group Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__name
+msgid "Group Name"
+msgstr "اسم المجموعة"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__users
+msgid "Group Users"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_tree
+msgid "Groups"
+msgstr "المجموعات"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_hierarchy
+msgid "Hierarchy Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
+msgid "ID"
+msgstr "المعرف"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor____last_update
+msgid "Last Modified on"
+msgstr "آخر تعديل في"
+
+#. module: muk_utils
+#: model:ir.actions.server,name:muk_utils.action_attachment_migrate
+msgid "Migrate"
+msgstr ""
+
+#. module: muk_utils
+#: code:addons/muk_utils/models/ir_attachment.py:87
+#, python-format
+msgid "Only administrators can execute this action."
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
+msgid "Parent Group"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_path
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path
+msgid "Parent Path"
+msgstr "المسار الأصلي"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_json
+msgid "Path Json"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_names
+msgid "Path Names"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_scss_editor
+msgid "Scss Editor"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_form
+msgid "Storage"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location
+msgid "Storage Location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location_changed
+msgid "Storage Location Changed"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_config_parameter
+msgid "System Parameter"
+msgstr "باراميتر النظام"
+
+#. module: muk_utils
+#: sql_constraint:muk_utils.mixins.groups:0
+msgid "The name of the group must be unique!"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__count_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Users"
+msgstr "المستخدمون"
+
diff --git a/muk_utils/i18n/de.po b/muk_utils/i18n/de.po
index 602621f..09af850 100644
--- a/muk_utils/i18n/de.po
+++ b/muk_utils/i18n/de.po
@@ -4,19 +4,16 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 12.0-20190128\n"
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-02-26 23:14+0000\n"
-"PO-Revision-Date: 2019-05-08 09:04+0000\n"
-"Last-Translator: Manuel Marklo \n"
-"Language-Team: German \n"
-"Language: de\n"
+"POT-Creation-Date: 2019-07-13 09:39+0000\n"
+"PO-Revision-Date: 2019-07-13 09:39+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.4\n"
+"Plural-Forms: \n"
#. module: muk_utils
#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
@@ -72,7 +69,7 @@ msgstr "Untergruppen"
#. module: muk_utils
#: model:ir.model,name:muk_utils.model_res_config_settings
msgid "Config Settings"
-msgstr "Konfiguration"
+msgstr "Konfiguration "
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__display_name
@@ -134,7 +131,7 @@ msgstr "Hierarchie Mixin"
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
msgid "ID"
-msgstr "ID"
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
@@ -149,16 +146,10 @@ msgid "Migrate"
msgstr "Migrieren"
#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:103
-#, python-format
-msgid "Migrate Attachment %s of %s to %s"
-msgstr "Migriere Dateianhang %s von %s nach %s"
-
-#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:85
+#: code:addons/muk_utils/models/ir_attachment.py:87
#, python-format
msgid "Only administrators can execute this action."
-msgstr "Nur Administratoren können diese Aktion ausführen."
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
@@ -184,7 +175,7 @@ msgstr "Pfadnamen"
#. module: muk_utils
#: model:ir.model,name:muk_utils.model_muk_utils_scss_editor
msgid "Scss Editor"
-msgstr "Scss Editor"
+msgstr ""
#. module: muk_utils
#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
@@ -217,3 +208,4 @@ msgstr "Der Name der Gruppe muss einzigartig sein!"
#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
msgid "Users"
msgstr "Benutzer"
+
diff --git a/muk_utils/i18n/es.po b/muk_utils/i18n/es.po
index 37e9a34..a79540b 100644
--- a/muk_utils/i18n/es.po
+++ b/muk_utils/i18n/es.po
@@ -4,19 +4,16 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 12.0-20190128\n"
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-02-26 23:15+0000\n"
-"PO-Revision-Date: 2019-06-27 07:53+0000\n"
-"Last-Translator: MuK IT \n"
-"Language-Team: Spanish \n"
-"Language: es\n"
+"POT-Creation-Date: 2019-07-13 09:39+0000\n"
+"PO-Revision-Date: 2019-07-13 09:39+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.7\n"
+"Plural-Forms: \n"
#. module: muk_utils
#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
@@ -61,7 +58,7 @@ msgstr "Adjuntos"
#. module: muk_utils
#: model:ir.model,name:muk_utils.model_base
msgid "Base"
-msgstr "Base"
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__child_groups
@@ -134,7 +131,7 @@ msgstr "Mezcla de jerarquías"
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
msgid "ID"
-msgstr "CARNÉ DE IDENTIDAD"
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
@@ -149,16 +146,10 @@ msgid "Migrate"
msgstr "Migrar"
#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:103
-#, python-format
-msgid "Migrate Attachment %s of %s to %s"
-msgstr "Migración de archivos adjuntos %s de %s a %s"
-
-#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:85
+#: code:addons/muk_utils/models/ir_attachment.py:87
#, python-format
msgid "Only administrators can execute this action."
-msgstr "Sólo los administradores pueden ejecutar esta acción."
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
@@ -217,3 +208,4 @@ msgstr "El nombre del grupo debe ser único!"
#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
msgid "Users"
msgstr "Usuarios"
+
diff --git a/muk_utils/i18n/fr.po b/muk_utils/i18n/fr.po
index a73cb76..c13768c 100644
--- a/muk_utils/i18n/fr.po
+++ b/muk_utils/i18n/fr.po
@@ -4,19 +4,16 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 12.0-20190128\n"
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-02-26 23:15+0000\n"
-"PO-Revision-Date: 2019-06-27 07:53+0000\n"
-"Last-Translator: MuK IT \n"
-"Language-Team: French \n"
-"Language: fr\n"
+"POT-Creation-Date: 2019-07-13 09:40+0000\n"
+"PO-Revision-Date: 2019-07-13 09:40+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 3.7\n"
+"Plural-Forms: \n"
#. module: muk_utils
#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
@@ -61,7 +58,7 @@ msgstr "Pièces jointes"
#. module: muk_utils
#: model:ir.model,name:muk_utils.model_base
msgid "Base"
-msgstr "Pièces jointes"
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__child_groups
@@ -134,7 +131,7 @@ msgstr "Groupes"
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
msgid "ID"
-msgstr "Hiérarchie Mixin"
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
@@ -149,16 +146,10 @@ msgid "Migrate"
msgstr "Dernière modification le"
#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:103
-#, python-format
-msgid "Migrate Attachment %s of %s to %s"
-msgstr "Migrer les %s de pièces jointes de %s vers %s"
-
-#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:85
+#: code:addons/muk_utils/models/ir_attachment.py:87
#, python-format
msgid "Only administrators can execute this action."
-msgstr "Seuls les administrateurs peuvent exécuter cette action."
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
@@ -217,3 +208,4 @@ msgstr "Changement de magasin"
#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
msgid "Users"
msgstr "Utilisateurs"
+
diff --git a/muk_utils/i18n/hi.po b/muk_utils/i18n/hi.po
new file mode 100644
index 0000000..2f56a20
--- /dev/null
+++ b/muk_utils/i18n/hi.po
@@ -0,0 +1,211 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * muk_utils
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-07-13 09:40+0000\n"
+"PO-Revision-Date: 2019-07-13 09:40+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Save this page before triggering the migration."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "All Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Field"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Model"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_attachment
+msgid "Attachment"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Attachment storage location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,help:muk_utils.field_res_config_settings__attachment_location
+msgid "Attachment storage location."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attachments"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_base
+msgid "Base"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__child_groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Child Groups"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_res_config_settings
+msgid "Config Settings"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__explicit_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Explicit Users"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Field Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Force Storage Migration"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Group"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_groups
+msgid "Group Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__name
+msgid "Group Name"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__users
+msgid "Group Users"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_tree
+msgid "Groups"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_hierarchy
+msgid "Hierarchy Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
+msgid "ID"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor____last_update
+msgid "Last Modified on"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.actions.server,name:muk_utils.action_attachment_migrate
+msgid "Migrate"
+msgstr ""
+
+#. module: muk_utils
+#: code:addons/muk_utils/models/ir_attachment.py:87
+#, python-format
+msgid "Only administrators can execute this action."
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
+msgid "Parent Group"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_path
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path
+msgid "Parent Path"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_json
+msgid "Path Json"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_names
+msgid "Path Names"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_scss_editor
+msgid "Scss Editor"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_form
+msgid "Storage"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location
+msgid "Storage Location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location_changed
+msgid "Storage Location Changed"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_config_parameter
+msgid "System Parameter"
+msgstr ""
+
+#. module: muk_utils
+#: sql_constraint:muk_utils.mixins.groups:0
+msgid "The name of the group must be unique!"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__count_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Users"
+msgstr ""
+
diff --git a/muk_utils/i18n/muk_utils.pot b/muk_utils/i18n/muk_utils.pot
index d18da49..5681cf0 100644
--- a/muk_utils/i18n/muk_utils.pot
+++ b/muk_utils/i18n/muk_utils.pot
@@ -4,10 +4,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 12.0-20190128\n"
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-02-26 23:14+0000\n"
-"PO-Revision-Date: 2019-02-26 23:14+0000\n"
+"POT-Creation-Date: 2019-07-13 09:39+0000\n"
+"PO-Revision-Date: 2019-07-13 09:39+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -146,13 +146,7 @@ msgid "Migrate"
msgstr ""
#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:103
-#, python-format
-msgid "Migrate Attachment %s of %s to %s"
-msgstr ""
-
-#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:85
+#: code:addons/muk_utils/models/ir_attachment.py:87
#, python-format
msgid "Only administrators can execute this action."
msgstr ""
diff --git a/muk_utils/i18n/nl.po b/muk_utils/i18n/nl.po
index 73b7c76..f6362cd 100644
--- a/muk_utils/i18n/nl.po
+++ b/muk_utils/i18n/nl.po
@@ -4,19 +4,16 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 12.0-20190128\n"
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-02-26 23:15+0000\n"
-"PO-Revision-Date: 2019-06-27 07:53+0000\n"
-"Last-Translator: MuK IT \n"
-"Language-Team: Dutch "
-"\n"
-"Language: nl\n"
+"POT-Creation-Date: 2019-07-13 09:40+0000\n"
+"PO-Revision-Date: 2019-07-13 09:40+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 3.7\n"
+"Plural-Forms: \n"
#. module: muk_utils
#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
@@ -79,7 +76,7 @@ msgstr "Configuratie instellingen"
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__display_name
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__display_name
msgid "Display Name"
-msgstr "Weergave naam"
+msgstr "Weergavenaam"
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__explicit_users
@@ -134,7 +131,7 @@ msgstr "Hiërarchie Mixin"
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
msgid "ID"
-msgstr "IDENTITEITSBEWIJS"
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
@@ -149,16 +146,10 @@ msgid "Migrate"
msgstr "Migreren"
#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:103
-#, python-format
-msgid "Migrate Attachment %s of %s to %s"
-msgstr "Migreer Attachment %s van %s naar %s"
-
-#. module: muk_utils
-#: code:addons/muk_utils/models/ir_attachment.py:85
+#: code:addons/muk_utils/models/ir_attachment.py:87
#, python-format
msgid "Only administrators can execute this action."
-msgstr "Alleen beheerders kunnen deze actie uitvoeren."
+msgstr ""
#. module: muk_utils
#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
@@ -217,3 +208,4 @@ msgstr "De naam van de groep moet uniek zijn!"
#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
msgid "Users"
msgstr "Gebruikers"
+
diff --git a/muk_utils/i18n/pt.po b/muk_utils/i18n/pt.po
new file mode 100644
index 0000000..d863b5e
--- /dev/null
+++ b/muk_utils/i18n/pt.po
@@ -0,0 +1,211 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * muk_utils
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-07-13 09:40+0000\n"
+"PO-Revision-Date: 2019-07-13 09:40+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Save this page before triggering the migration."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "All Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Field"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Model"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_attachment
+msgid "Attachment"
+msgstr "Anexo"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Attachment storage location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,help:muk_utils.field_res_config_settings__attachment_location
+msgid "Attachment storage location."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attachments"
+msgstr "Anexos"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_base
+msgid "Base"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__child_groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Child Groups"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_res_config_settings
+msgid "Config Settings"
+msgstr "config configurações"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__display_name
+msgid "Display Name"
+msgstr "Nome a Exibir"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__explicit_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Explicit Users"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Field Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Force Storage Migration"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Group"
+msgstr "Grupo"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_groups
+msgid "Group Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__name
+msgid "Group Name"
+msgstr "Nome do Grupo"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__users
+msgid "Group Users"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_tree
+msgid "Groups"
+msgstr "Grupos"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_hierarchy
+msgid "Hierarchy Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
+msgid "ID"
+msgstr "Id."
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor____last_update
+msgid "Last Modified on"
+msgstr "Última Modificação em"
+
+#. module: muk_utils
+#: model:ir.actions.server,name:muk_utils.action_attachment_migrate
+msgid "Migrate"
+msgstr ""
+
+#. module: muk_utils
+#: code:addons/muk_utils/models/ir_attachment.py:87
+#, python-format
+msgid "Only administrators can execute this action."
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
+msgid "Parent Group"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_path
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path
+msgid "Parent Path"
+msgstr "Caminho ascendente "
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_json
+msgid "Path Json"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_names
+msgid "Path Names"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_scss_editor
+msgid "Scss Editor"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_form
+msgid "Storage"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location
+msgid "Storage Location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location_changed
+msgid "Storage Location Changed"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_config_parameter
+msgid "System Parameter"
+msgstr ""
+
+#. module: muk_utils
+#: sql_constraint:muk_utils.mixins.groups:0
+msgid "The name of the group must be unique!"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__count_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Users"
+msgstr "Utilizadores"
+
diff --git a/muk_utils/i18n/ru.po b/muk_utils/i18n/ru.po
new file mode 100644
index 0000000..24fee85
--- /dev/null
+++ b/muk_utils/i18n/ru.po
@@ -0,0 +1,211 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * muk_utils
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 12.0-20190522\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-07-13 09:40+0000\n"
+"PO-Revision-Date: 2019-07-13 09:40+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Save this page before triggering the migration."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "All Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Field"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attached Document Model"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_attachment
+msgid "Attachment"
+msgstr "Приложение"
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Attachment storage location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,help:muk_utils.field_res_config_settings__attachment_location
+msgid "Attachment storage location."
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Attachments"
+msgstr "Вложения"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_base
+msgid "Base"
+msgstr "Базовый"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__child_groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Child Groups"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_res_config_settings
+msgid "Config Settings"
+msgstr "Настройки конфигурации"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__display_name
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__display_name
+msgid "Display Name"
+msgstr "Отображаемое Имя"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__explicit_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Explicit Users"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_search
+msgid "Field Data"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+msgid "Force Storage Migration"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Group"
+msgstr "Группа"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_groups
+msgid "Group Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__name
+msgid "Group Name"
+msgstr "Наименование Группы"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__users
+msgid "Group Users"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__groups
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_tree
+msgid "Groups"
+msgstr "Группы"
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_mixins_hierarchy
+msgid "Hierarchy Mixin"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__id
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor__id
+msgid "ID"
+msgstr "Номер"
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy____last_update
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_scss_editor____last_update
+msgid "Last Modified on"
+msgstr "Последнее изменение"
+
+#. module: muk_utils
+#: model:ir.actions.server,name:muk_utils.action_attachment_migrate
+msgid "Migrate"
+msgstr ""
+
+#. module: muk_utils
+#: code:addons/muk_utils/models/ir_attachment.py:87
+#, python-format
+msgid "Only administrators can execute this action."
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_group
+msgid "Parent Group"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__parent_path
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path
+msgid "Parent Path"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_json
+msgid "Path Json"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_hierarchy__parent_path_names
+msgid "Path Names"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_muk_utils_scss_editor
+msgid "Scss Editor"
+msgstr ""
+
+#. module: muk_utils
+#: model_terms:ir.ui.view,arch_db:muk_utils.res_config_settings_view_form
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_attachment_form
+msgid "Storage"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location
+msgid "Storage Location"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_res_config_settings__attachment_location_changed
+msgid "Storage Location Changed"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model,name:muk_utils.model_ir_config_parameter
+msgid "System Parameter"
+msgstr ""
+
+#. module: muk_utils
+#: sql_constraint:muk_utils.mixins.groups:0
+msgid "The name of the group must be unique!"
+msgstr ""
+
+#. module: muk_utils
+#: model:ir.model.fields,field_description:muk_utils.field_muk_utils_mixins_groups__count_users
+#: model_terms:ir.ui.view,arch_db:muk_utils.view_mixins_groups_form
+msgid "Users"
+msgstr "Пользователи"
+
diff --git a/muk_utils/models/__init__.py b/muk_utils/models/__init__.py
index 896072c..424dbbe 100644
--- a/muk_utils/models/__init__.py
+++ b/muk_utils/models/__init__.py
@@ -1,19 +1,22 @@
###################################################################################
#
-# Copyright (C) 2018 MuK IT GmbH
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
diff --git a/muk_utils/models/base.py b/muk_utils/models/base.py
index f857e60..8e5039f 100644
--- a/muk_utils/models/base.py
+++ b/muk_utils/models/base.py
@@ -1,183 +1,186 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import logging
-
-from odoo import api, models, fields
-from odoo.osv import expression
-
-from odoo.addons.muk_utils.tools import utils
-
-_logger = logging.getLogger(__name__)
-
-class Base(models.AbstractModel):
-
- _inherit = 'base'
-
- #----------------------------------------------------------
- # Helper Methods
- #----------------------------------------------------------
-
- @api.model
- def _check_parent_field(self):
- if self._parent_name not in self._fields:
- raise TypeError("The parent (%s) field does not exist." % self._parent_name)
-
- @api.model
- def _build_search_childs_domain(self, parent_id, domain=[]):
- self._check_parent_field()
- parent_domain = [[self._parent_name, '=', parent_id]]
- return expression.AND([parent_domain, domain]) if domain else parent_domain
-
- @api.model
- def _check_context_bin_size(self, field):
- return any(key in self.env.context for key in ['bin_size', 'bin_size_%s' % (field)])
-
- #----------------------------------------------------------
- # Security
- #----------------------------------------------------------
-
- @api.multi
- def _filter_access(self, operation):
- if self.check_access_rights(operation, False):
- return self._filter_access_rules(operation)
- return self.env[self._name]
-
- @api.multi
- def _filter_access_ids(self, operation):
- return self._filter_access(operation).ids
-
- @api.multi
- def check_access(self, operation, raise_exception=False):
- try:
- access_right = self.check_access_rights(operation, raise_exception)
- access_rule = self.check_access_rule(operation) is None
- return access_right and access_rule
- except AccessError:
- if raise_exception:
- raise
- return False
-
- #----------------------------------------------------------
- # Hierarchy Methods
- #----------------------------------------------------------
-
- @api.model
- def search_parents(self, domain=[], offset=0, limit=None, order=None, count=False):
- """ This method finds the top level elements of the hierarchy for a given search query.
-
- :param domain: a search domain (default: empty list)
- :param order: a string to define the sort order of the query (default: none)
- :returns: the top level elements for the given search query
- """
- res = self._search_parents(domain=domain, offset=offset, limit=limit, order=order, count=count)
- return res if count else self.browse(res)
-
- @api.model
- def search_read_parents(self, domain=[], fields=None, offset=0, limit=None, order=None):
- """ This method finds the top level elements of the hierarchy for a given search query.
-
- :param domain: a search domain (default: empty list)
- :param fields: a list of fields to read (default: all fields of the model)
- :param order: a string to define the sort order of the query (default: none)
- :returns: the top level elements for the given search query
- """
- records = self.search_parents(domain=domain, offset=offset, limit=limit, order=order)
- if not records:
- return []
- if fields and fields == ['id']:
- return [{'id': record.id} for record in records]
- result = records.read(fields)
- if len(result) <= 1:
- return result
- index = {vals['id']: vals for vals in result}
- return [index[record.id] for record in records if record.id in index]
-
- @api.model
- def _search_parents(self, domain=[], offset=0, limit=None, order=None, count=False):
- self._check_parent_field()
- self.check_access_rights('read')
- if expression.is_false(self, domain):
- return []
- query = self._where_calc(domain)
- self._apply_ir_rules(query, 'read')
- from_clause, where_clause, where_clause_arguments = query.get_sql()
- parent_where = where_clause and (" WHERE %s" % where_clause) or ''
- parent_query = 'SELECT "%s".id FROM ' % self._table + from_clause + parent_where
- no_parent_clause ='"{table}"."{field}" IS NULL'.format(
- table=self._table,
- field=self._parent_name
- )
- no_access_clause ='"{table}"."{field}" NOT IN ({query})'.format(
- table=self._table,
- field=self._parent_name,
- query=parent_query
- )
- parent_clause = '({0} OR {1})'.format(
- no_parent_clause,
- no_access_clause
- )
- order_by = self._generate_order_by(order, query)
- from_clause, where_clause, where_clause_params = query.get_sql()
- where_str = (
- where_clause and
- (" WHERE %s AND %s" % (where_clause, parent_clause)) or
- (" WHERE %s" % parent_clause)
- )
- if count:
- query_str = 'SELECT count(1) FROM ' + from_clause + where_str
- self._cr.execute(query_str, where_clause_params)
- return self._cr.fetchone()[0]
- limit_str = limit and ' limit %d' % limit or ''
- offset_str = offset and ' offset %d' % offset or ''
- query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
- complete_where_clause_params = where_clause_params + where_clause_arguments
- self._cr.execute(query_str, complete_where_clause_params)
- return utils.uniquify_list([x[0] for x in self._cr.fetchall()])
-
- @api.model
- def search_childs(self, parent_id, domain=[], offset=0, limit=None, order=None, count=False):
- """ This method finds the direct child elements of the parent record for a given search query.
-
- :param parent_id: the integer representing the ID of the parent record
- :param domain: a search domain (default: empty list)
- :param offset: the number of results to ignore (default: none)
- :param limit: maximum number of records to return (default: all)
- :param order: a string to define the sort order of the query (default: none)
- :param count: counts and returns the number of matching records (default: False)
- :returns: the top level elements for the given search query
- """
- domain = self._build_search_childs_domain(parent_id, domain=domain)
- return self.search(domain, offset=offset, limit=limit, order=order, count=count)
-
- @api.model
- def search_read_childs(self, parent_id, domain=[], fields=None, offset=0, limit=None, order=None):
- """ This method finds the direct child elements of the parent record for a given search query.
-
- :param parent_id: the integer representing the ID of the parent record
- :param domain: a search domain (default: empty list)
- :param fields: a list of fields to read (default: all fields of the model)
- :param offset: the number of results to ignore (default: none)
- :param limit: maximum number of records to return (default: all)
- :param order: a string to define the sort order of the query (default: none)
- :returns: the top level elements for the given search query
- """
- domain = self._build_search_childs_domain(parent_id, domain=domain)
- return self.search_read(domain=domain, fields=fields, offset=offset, limit=limit, order=order)
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import logging
+
+from odoo import api, models, fields
+from odoo.osv import expression
+
+from odoo.addons.muk_utils.tools import utils
+
+_logger = logging.getLogger(__name__)
+
+class Base(models.AbstractModel):
+
+ _inherit = 'base'
+
+ #----------------------------------------------------------
+ # Helper Methods
+ #----------------------------------------------------------
+
+ @api.model
+ def _check_parent_field(self):
+ if self._parent_name not in self._fields:
+ raise TypeError("The parent (%s) field does not exist." % self._parent_name)
+
+ @api.model
+ def _build_search_childs_domain(self, parent_id, domain=[]):
+ self._check_parent_field()
+ parent_domain = [[self._parent_name, '=', parent_id]]
+ return expression.AND([parent_domain, domain]) if domain else parent_domain
+
+ @api.model
+ def _check_context_bin_size(self, field):
+ return any(key in self.env.context for key in ['bin_size', 'bin_size_%s' % (field)])
+
+ #----------------------------------------------------------
+ # Security
+ #----------------------------------------------------------
+
+ @api.multi
+ def _filter_access(self, operation):
+ if self.check_access_rights(operation, False):
+ return self._filter_access_rules(operation)
+ return self.env[self._name]
+
+ @api.multi
+ def _filter_access_ids(self, operation):
+ return self._filter_access(operation).ids
+
+ @api.multi
+ def check_access(self, operation, raise_exception=False):
+ try:
+ access_right = self.check_access_rights(operation, raise_exception)
+ access_rule = self.check_access_rule(operation) is None
+ return access_right and access_rule
+ except AccessError:
+ if raise_exception:
+ raise
+ return False
+
+ #----------------------------------------------------------
+ # Hierarchy Methods
+ #----------------------------------------------------------
+
+ @api.model
+ def search_parents(self, domain=[], offset=0, limit=None, order=None, count=False):
+ """ This method finds the top level elements of the hierarchy for a given search query.
+
+ :param domain: a search domain (default: empty list)
+ :param order: a string to define the sort order of the query (default: none)
+ :returns: the top level elements for the given search query
+ """
+ res = self._search_parents(domain=domain, offset=offset, limit=limit, order=order, count=count)
+ return res if count else self.browse(res)
+
+ @api.model
+ def search_read_parents(self, domain=[], fields=None, offset=0, limit=None, order=None):
+ """ This method finds the top level elements of the hierarchy for a given search query.
+
+ :param domain: a search domain (default: empty list)
+ :param fields: a list of fields to read (default: all fields of the model)
+ :param order: a string to define the sort order of the query (default: none)
+ :returns: the top level elements for the given search query
+ """
+ records = self.search_parents(domain=domain, offset=offset, limit=limit, order=order)
+ if not records:
+ return []
+ if fields and fields == ['id']:
+ return [{'id': record.id} for record in records]
+ result = records.read(fields)
+ if len(result) <= 1:
+ return result
+ index = {vals['id']: vals for vals in result}
+ return [index[record.id] for record in records if record.id in index]
+
+ @api.model
+ def _search_parents(self, domain=[], offset=0, limit=None, order=None, count=False):
+ self._check_parent_field()
+ self.check_access_rights('read')
+ if expression.is_false(self, domain):
+ return []
+ query = self._where_calc(domain)
+ self._apply_ir_rules(query, 'read')
+ from_clause, where_clause, where_clause_arguments = query.get_sql()
+ parent_where = where_clause and (" WHERE %s" % where_clause) or ''
+ parent_query = 'SELECT "%s".id FROM ' % self._table + from_clause + parent_where
+ no_parent_clause ='"{table}"."{field}" IS NULL'.format(
+ table=self._table,
+ field=self._parent_name
+ )
+ no_access_clause ='"{table}"."{field}" NOT IN ({query})'.format(
+ table=self._table,
+ field=self._parent_name,
+ query=parent_query
+ )
+ parent_clause = '({0} OR {1})'.format(
+ no_parent_clause,
+ no_access_clause
+ )
+ order_by = self._generate_order_by(order, query)
+ from_clause, where_clause, where_clause_params = query.get_sql()
+ where_str = (
+ where_clause and
+ (" WHERE %s AND %s" % (where_clause, parent_clause)) or
+ (" WHERE %s" % parent_clause)
+ )
+ if count:
+ query_str = 'SELECT count(1) FROM ' + from_clause + where_str
+ self._cr.execute(query_str, where_clause_params)
+ return self._cr.fetchone()[0]
+ limit_str = limit and ' limit %d' % limit or ''
+ offset_str = offset and ' offset %d' % offset or ''
+ query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
+ complete_where_clause_params = where_clause_params + where_clause_arguments
+ self._cr.execute(query_str, complete_where_clause_params)
+ return utils.uniquify_list([x[0] for x in self._cr.fetchall()])
+
+ @api.model
+ def search_childs(self, parent_id, domain=[], offset=0, limit=None, order=None, count=False):
+ """ This method finds the direct child elements of the parent record for a given search query.
+
+ :param parent_id: the integer representing the ID of the parent record
+ :param domain: a search domain (default: empty list)
+ :param offset: the number of results to ignore (default: none)
+ :param limit: maximum number of records to return (default: all)
+ :param order: a string to define the sort order of the query (default: none)
+ :param count: counts and returns the number of matching records (default: False)
+ :returns: the top level elements for the given search query
+ """
+ domain = self._build_search_childs_domain(parent_id, domain=domain)
+ return self.search(domain, offset=offset, limit=limit, order=order, count=count)
+
+ @api.model
+ def search_read_childs(self, parent_id, domain=[], fields=None, offset=0, limit=None, order=None):
+ """ This method finds the direct child elements of the parent record for a given search query.
+
+ :param parent_id: the integer representing the ID of the parent record
+ :param domain: a search domain (default: empty list)
+ :param fields: a list of fields to read (default: all fields of the model)
+ :param offset: the number of results to ignore (default: none)
+ :param limit: maximum number of records to return (default: all)
+ :param order: a string to define the sort order of the query (default: none)
+ :returns: the top level elements for the given search query
+ """
+ domain = self._build_search_childs_domain(parent_id, domain=domain)
+ return self.search_read(domain=domain, fields=fields, offset=offset, limit=limit, order=order)
\ No newline at end of file
diff --git a/muk_utils/models/ir_attachment.py b/muk_utils/models/ir_attachment.py
index 33591c4..9fc298f 100644
--- a/muk_utils/models/ir_attachment.py
+++ b/muk_utils/models/ir_attachment.py
@@ -1,29 +1,34 @@
###################################################################################
#
-# Copyright (C) 2018 MuK IT GmbH
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
+import math
import base64
import logging
import mimetypes
-from odoo import api, models, _
-from odoo.exceptions import AccessError
+from odoo import registry, api, models, _
from odoo.tools.mimetypes import guess_mimetype
+from odoo.tools.misc import split_every
+from odoo.exceptions import AccessError
_logger = logging.getLogger(__name__)
@@ -97,12 +102,30 @@ class IrAttachment(models.Model):
@api.multi
def migrate(self):
- record_count = len(self)
- storage = self._storage().upper()
- for index, attach in enumerate(self):
- _logger.info(_("Migrate Attachment %s of %s to %s") % (index + 1, record_count, storage))
- attach.with_context(migration=True).write({'datas': attach.datas})
-
+ storage_location = self._storage().upper()
+ batch_size = self.env.context.get('migration_batch_size', 100)
+ batches_to_migrate = math.ceil(len(self) / batch_size)
+ for batch_index, sub_ids in enumerate(split_every(batch_size, self.ids)):
+ with api.Environment.manage():
+ with registry(self.env.cr.dbname).cursor() as batch_cr:
+ batch_env = api.Environment(batch_cr, self.env.uid, self.env.context.copy())
+ attachment_records = batch_env['ir.attachment'].browse(sub_ids)
+ batch_records_count = len(attachment_records)
+ try:
+ for index, attach in enumerate(attachment_records):
+ _logger.info("Migrate Attachment %s of %s to %s [Batch %s of %s]",
+ index + 1, batch_records_count, storage_location,
+ batch_index + 1, batches_to_migrate
+ )
+ attach.with_context(migration=True).write({
+ 'datas': attach.datas
+ })
+ except:
+ batch_cr.rollback()
+ raise
+ else:
+ batch_cr.commit()
+
#----------------------------------------------------------
# Read
#----------------------------------------------------------
diff --git a/muk_utils/models/ir_config_parameter.py b/muk_utils/models/ir_config_parameter.py
index 15804e7..918feb4 100644
--- a/muk_utils/models/ir_config_parameter.py
+++ b/muk_utils/models/ir_config_parameter.py
@@ -1,29 +1,32 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# 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 .
-#
-###################################################################################
-
-from odoo import models, api
-
-class IrConfigParameter(models.Model):
-
- _inherit = 'ir.config_parameter'
-
- @api.model
- def set_params(self, params):
- for key, value in params.items():
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from odoo import models, api
+
+class IrConfigParameter(models.Model):
+
+ _inherit = 'ir.config_parameter'
+
+ @api.model
+ def set_params(self, params):
+ for key, value in params.items():
self.set_param(key, value)
\ No newline at end of file
diff --git a/muk_utils/models/mixins_groups.py b/muk_utils/models/mixins_groups.py
index 83a481a..202db0c 100644
--- a/muk_utils/models/mixins_groups.py
+++ b/muk_utils/models/mixins_groups.py
@@ -1,123 +1,126 @@
-###################################################################################
-#
-# Copyright (C) 2017 MuK IT GmbH
-#
-# 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 .
-#
-###################################################################################
-
-from odoo import models, fields, api
-
-class Groups(models.AbstractModel):
-
- _name = 'muk_utils.mixins.groups'
- _description = 'Group Mixin'
-
- _parent_store = True
- _parent_name = "parent_group"
-
- #----------------------------------------------------------
- # Database
- #----------------------------------------------------------
-
- name = fields.Char(
- string="Group Name",
- required=True,
- translate=True)
-
- parent_path = fields.Char(
- string="Parent Path",
- index=True)
-
- count_users = fields.Integer(
- compute='_compute_users',
- string="Users",
- store=True)
-
- @api.model
- def _add_magic_fields(self):
- super(Groups, self)._add_magic_fields()
- def add(name, field):
- if name not in self._fields:
- self._add_field(name, field)
- add('parent_group', fields.Many2one(
- _module=self._module,
- comodel_name=self._name,
- string='Parent Group',
- ondelete='cascade',
- auto_join=True,
- index=True,
- automatic=True))
- add('child_groups', fields.One2many(
- _module=self._module,
- comodel_name=self._name,
- inverse_name='parent_group',
- string='Child Groups',
- automatic=True))
- add('groups', fields.Many2many(
- _module=self._module,
- comodel_name='res.groups',
- relation='%s_groups_rel' % (self._table),
- column1='gid',
- column2='rid',
- string='Groups',
- automatic=True))
- add('explicit_users', fields.Many2many(
- _module=self._module,
- comodel_name='res.users',
- relation='%s_explicit_users_rel' % (self._table),
- column1='gid',
- column2='uid',
- string='Explicit Users',
- automatic=True))
- add('users', fields.Many2many(
- _module=self._module,
- comodel_name='res.users',
- relation='%s_users_rel' % (self._table),
- column1='gid',
- column2='uid',
- string='Group Users',
- compute='_compute_users',
- store=True,
- automatic=True))
-
- _sql_constraints = [
- ('name_uniq', 'unique (name)', 'The name of the group must be unique!')
- ]
-
- #----------------------------------------------------------
- # Functions
- #----------------------------------------------------------
-
- @api.model
- def default_get(self, fields_list):
- res = super(Groups, self).default_get(fields_list)
- if not self.env.context.get('groups_no_autojoin'):
- if 'explicit_users' in res and res['explicit_users']:
- res['explicit_users'] = res['explicit_users'] + [self.env.uid]
- else:
- res['explicit_users'] = [self.env.uid]
- return res
-
- #----------------------------------------------------------
- # Read, View
- #----------------------------------------------------------
-
- @api.depends('parent_group', 'parent_group.users', 'groups', 'groups.users', 'explicit_users')
- def _compute_users(self):
- for record in self:
- users = record.mapped('groups.users')
- users |= record.mapped('explicit_users')
- users |= record.mapped('parent_group.users')
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+from odoo import models, fields, api
+
+class Groups(models.AbstractModel):
+
+ _name = 'muk_utils.mixins.groups'
+ _description = 'Group Mixin'
+
+ _parent_store = True
+ _parent_name = "parent_group"
+
+ #----------------------------------------------------------
+ # Database
+ #----------------------------------------------------------
+
+ name = fields.Char(
+ string="Group Name",
+ required=True,
+ translate=True)
+
+ parent_path = fields.Char(
+ string="Parent Path",
+ index=True)
+
+ count_users = fields.Integer(
+ compute='_compute_users',
+ string="Users",
+ store=True)
+
+ @api.model
+ def _add_magic_fields(self):
+ super(Groups, self)._add_magic_fields()
+ def add(name, field):
+ if name not in self._fields:
+ self._add_field(name, field)
+ add('parent_group', fields.Many2one(
+ _module=self._module,
+ comodel_name=self._name,
+ string='Parent Group',
+ ondelete='cascade',
+ auto_join=True,
+ index=True,
+ automatic=True))
+ add('child_groups', fields.One2many(
+ _module=self._module,
+ comodel_name=self._name,
+ inverse_name='parent_group',
+ string='Child Groups',
+ automatic=True))
+ add('groups', fields.Many2many(
+ _module=self._module,
+ comodel_name='res.groups',
+ relation='%s_groups_rel' % (self._table),
+ column1='gid',
+ column2='rid',
+ string='Groups',
+ automatic=True))
+ add('explicit_users', fields.Many2many(
+ _module=self._module,
+ comodel_name='res.users',
+ relation='%s_explicit_users_rel' % (self._table),
+ column1='gid',
+ column2='uid',
+ string='Explicit Users',
+ automatic=True))
+ add('users', fields.Many2many(
+ _module=self._module,
+ comodel_name='res.users',
+ relation='%s_users_rel' % (self._table),
+ column1='gid',
+ column2='uid',
+ string='Group Users',
+ compute='_compute_users',
+ store=True,
+ automatic=True))
+
+ _sql_constraints = [
+ ('name_uniq', 'unique (name)', 'The name of the group must be unique!')
+ ]
+
+ #----------------------------------------------------------
+ # Functions
+ #----------------------------------------------------------
+
+ @api.model
+ def default_get(self, fields_list):
+ res = super(Groups, self).default_get(fields_list)
+ if not self.env.context.get('groups_no_autojoin'):
+ if 'explicit_users' in res and res['explicit_users']:
+ res['explicit_users'] = res['explicit_users'] + [self.env.uid]
+ else:
+ res['explicit_users'] = [self.env.uid]
+ return res
+
+ #----------------------------------------------------------
+ # Read, View
+ #----------------------------------------------------------
+
+ @api.depends('parent_group', 'parent_group.users', 'groups', 'groups.users', 'explicit_users')
+ def _compute_users(self):
+ for record in self:
+ users = record.mapped('groups.users')
+ users |= record.mapped('explicit_users')
+ users |= record.mapped('parent_group.users')
record.update({'users': users, 'count_users': len(users)})
\ No newline at end of file
diff --git a/muk_utils/models/mixins_hierarchy.py b/muk_utils/models/mixins_hierarchy.py
index 3ba620e..8f282ab 100644
--- a/muk_utils/models/mixins_hierarchy.py
+++ b/muk_utils/models/mixins_hierarchy.py
@@ -1,173 +1,176 @@
-###################################################################################
-#
-# Copyright (C) 2017 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import json
-import operator
-import functools
-import collections
-
-from odoo import models, fields, api
-from odoo.osv import expression
-
-class Hierarchy(models.AbstractModel):
-
- _name = 'muk_utils.mixins.hierarchy'
- _description = 'Hierarchy Mixin'
-
- _parent_store = True
- _parent_path_sudo = False
- _parent_path_store = False
-
- _name_path_context = "show_path"
-
- #----------------------------------------------------------
- # Database
- #----------------------------------------------------------
-
- parent_path = fields.Char(
- string="Parent Path",
- index=True)
-
- @api.model
- def _add_magic_fields(self):
- super(Hierarchy, self)._add_magic_fields()
- def add(name, field):
- if name not in self._fields:
- self._add_field(name, field)
- path_names_search = None
- if not self._parent_path_store:
- path_names_search = '_search_parent_path_names'
- add('parent_path_names', fields.Char(
- _module=self._module,
- compute='_compute_parent_paths',
- compute_sudo=self._parent_path_sudo,
- store=self._parent_path_store,
- search=path_names_search,
- string="Path Names",
- readonly=True,
- automatic=True))
- add('parent_path_json', fields.Text(
- _module=self._module,
- compute='_compute_parent_paths',
- compute_sudo=self._parent_path_sudo,
- store=self._parent_path_store,
- string="Path Json",
- readonly=True,
- automatic=True))
-
- #----------------------------------------------------------
- # Helper
- #----------------------------------------------------------
-
- def _get_depends_parent_paths(self):
- depends = ['parent_path']
- if self._rec_name:
- depends += [self._rec_name]
- elif 'name' in self._fields:
- depends += ['name']
- elif 'x_name' in self._fields:
- depends += ['x_name']
- return depends
-
- #----------------------------------------------------------
- # Search
- #----------------------------------------------------------
-
- @api.model
- def _search_parent_path_names(self, operator, operand):
- domain = []
- for value in operand.split('/'):
- args = [(self._rec_name_fallback(), operator, value)]
- domain = expression.OR([args, domain]) if domain else args
- return domain if domain else [(self._rec_name_fallback(), operator, "")]
-
- #----------------------------------------------------------
- # Read, View
- #----------------------------------------------------------
-
- @api.depends(lambda self: self._get_depends_parent_paths())
- def _compute_parent_paths(self):
- records = self.filtered(lambda record: record.parent_path)
- paths = [list(map(int, rec.parent_path.split('/')[:-1])) for rec in records]
- ids = paths and set(functools.reduce(operator.concat, paths)) or []
- model_without_path = self.with_context(**{self._name_path_context: False})
- filtered_records = model_without_path.browse(ids)._filter_access('read')
- data = dict(filtered_records.name_get())
- for record in records:
- path_names = [""]
- path_json = []
- for id in reversed(list(map(int, record.parent_path.split('/')[:-1]))):
- if id not in data:
- break
- path_names.append(data[id])
- path_json.append({
- 'model': record._name,
- 'name': data[id],
- 'id': id,
- })
- path_names.reverse()
- path_json.reverse()
- record.update({
- 'parent_path_names': '/'.join(path_names),
- 'parent_path_json': json.dumps(path_json),
- })
-
- @api.model
- def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
- domain = list(args or [])
- if not (name == '' and operator == 'ilike') :
- if '/' in name:
- domain += [('parent_path_names', operator, name)]
- else:
- domain += [(self._rec_name, operator, name)]
- records = self.browse(self._search(domain, limit=limit, access_rights_uid=name_get_uid))
- return models.lazy_name_get(records.sudo(name_get_uid or self.env.uid))
-
- @api.multi
- def name_get(self):
- if self.env.context.get(self._name_path_context):
- res = []
- for record in self:
- names = record.parent_path_names
- if not names:
- res.append(super(Hierarchy, record).name_get()[0])
- elif not len(names) > 50:
- res.append((record.id, names))
- else:
- res.append((record.id, ".." + names[-48:]))
- return res
- return super(Hierarchy, self).name_get()
-
- #----------------------------------------------------------
- # Create, Update, Delete
- #----------------------------------------------------------
-
- @api.multi
- def write(self, vals):
- if self._parent_path_store and self._rec_name_fallback() in vals:
- with self.env.norecompute():
- res = super(Hierarchy, self).write(vals)
- domain = [('id', 'child_of', self.ids)]
- records = self.sudo().search(domain)
- records.modified(['parent_path'])
- if self.env.recompute and self.env.context.get('recompute', True):
- records.recompute()
- return res
- return super(Hierarchy, self).write(vals)
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import json
+import operator
+import functools
+import collections
+
+from odoo import models, fields, api
+from odoo.osv import expression
+
+class Hierarchy(models.AbstractModel):
+
+ _name = 'muk_utils.mixins.hierarchy'
+ _description = 'Hierarchy Mixin'
+
+ _parent_store = True
+ _parent_path_sudo = False
+ _parent_path_store = False
+
+ _name_path_context = "show_path"
+
+ #----------------------------------------------------------
+ # Database
+ #----------------------------------------------------------
+
+ parent_path = fields.Char(
+ string="Parent Path",
+ index=True)
+
+ @api.model
+ def _add_magic_fields(self):
+ super(Hierarchy, self)._add_magic_fields()
+ def add(name, field):
+ if name not in self._fields:
+ self._add_field(name, field)
+ path_names_search = None
+ if not self._parent_path_store:
+ path_names_search = '_search_parent_path_names'
+ add('parent_path_names', fields.Char(
+ _module=self._module,
+ compute='_compute_parent_paths',
+ compute_sudo=self._parent_path_sudo,
+ store=self._parent_path_store,
+ search=path_names_search,
+ string="Path Names",
+ readonly=True,
+ automatic=True))
+ add('parent_path_json', fields.Text(
+ _module=self._module,
+ compute='_compute_parent_paths',
+ compute_sudo=self._parent_path_sudo,
+ store=self._parent_path_store,
+ string="Path Json",
+ readonly=True,
+ automatic=True))
+
+ #----------------------------------------------------------
+ # Helper
+ #----------------------------------------------------------
+
+ def _get_depends_parent_paths(self):
+ depends = ['parent_path']
+ if self._rec_name:
+ depends += [self._rec_name]
+ elif 'name' in self._fields:
+ depends += ['name']
+ elif 'x_name' in self._fields:
+ depends += ['x_name']
+ return depends
+
+ #----------------------------------------------------------
+ # Search
+ #----------------------------------------------------------
+
+ @api.model
+ def _search_parent_path_names(self, operator, operand):
+ domain = []
+ for value in operand.split('/'):
+ args = [(self._rec_name_fallback(), operator, value)]
+ domain = expression.OR([args, domain]) if domain else args
+ return domain if domain else [(self._rec_name_fallback(), operator, "")]
+
+ #----------------------------------------------------------
+ # Read, View
+ #----------------------------------------------------------
+
+ @api.depends(lambda self: self._get_depends_parent_paths())
+ def _compute_parent_paths(self):
+ records = self.filtered(lambda record: record.parent_path)
+ paths = [list(map(int, rec.parent_path.split('/')[:-1])) for rec in records]
+ ids = paths and set(functools.reduce(operator.concat, paths)) or []
+ model_without_path = self.with_context(**{self._name_path_context: False})
+ filtered_records = model_without_path.browse(ids)._filter_access('read')
+ data = dict(filtered_records.name_get())
+ for record in records:
+ path_names = [""]
+ path_json = []
+ for id in reversed(list(map(int, record.parent_path.split('/')[:-1]))):
+ if id not in data:
+ break
+ path_names.append(data[id])
+ path_json.append({
+ 'model': record._name,
+ 'name': data[id],
+ 'id': id,
+ })
+ path_names.reverse()
+ path_json.reverse()
+ record.update({
+ 'parent_path_names': '/'.join(path_names),
+ 'parent_path_json': json.dumps(path_json),
+ })
+
+ @api.model
+ def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
+ domain = list(args or [])
+ if not (name == '' and operator == 'ilike') :
+ if '/' in name:
+ domain += [('parent_path_names', operator, name)]
+ else:
+ domain += [(self._rec_name, operator, name)]
+ records = self.browse(self._search(domain, limit=limit, access_rights_uid=name_get_uid))
+ return models.lazy_name_get(records.sudo(name_get_uid or self.env.uid))
+
+ @api.multi
+ def name_get(self):
+ if self.env.context.get(self._name_path_context):
+ res = []
+ for record in self:
+ names = record.parent_path_names
+ if not names:
+ res.append(super(Hierarchy, record).name_get()[0])
+ elif not len(names) > 50:
+ res.append((record.id, names))
+ else:
+ res.append((record.id, ".." + names[-48:]))
+ return res
+ return super(Hierarchy, self).name_get()
+
+ #----------------------------------------------------------
+ # Create, Update, Delete
+ #----------------------------------------------------------
+
+ @api.multi
+ def write(self, vals):
+ if self._parent_path_store and self._rec_name_fallback() in vals:
+ with self.env.norecompute():
+ res = super(Hierarchy, self).write(vals)
+ domain = [('id', 'child_of', self.ids)]
+ records = self.sudo().search(domain)
+ records.modified(['parent_path'])
+ if self.env.recompute and self.env.context.get('recompute', True):
+ records.recompute()
+ return res
+ return super(Hierarchy, self).write(vals)
\ No newline at end of file
diff --git a/muk_utils/models/res_config_settings.py b/muk_utils/models/res_config_settings.py
index dde75fa..3566324 100644
--- a/muk_utils/models/res_config_settings.py
+++ b/muk_utils/models/res_config_settings.py
@@ -1,19 +1,22 @@
###################################################################################
#
-# Copyright (C) 2017 MuK IT GmbH
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
diff --git a/muk_utils/models/scss_editor.py b/muk_utils/models/scss_editor.py
index 39129f9..dd5fd18 100644
--- a/muk_utils/models/scss_editor.py
+++ b/muk_utils/models/scss_editor.py
@@ -1,19 +1,22 @@
###################################################################################
-#
-# Copyright (C) 2017 MuK IT GmbH
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
diff --git a/muk_utils/tests/__init__.py b/muk_utils/tests/__init__.py
index 9ee5a73..2fb4f4b 100644
--- a/muk_utils/tests/__init__.py
+++ b/muk_utils/tests/__init__.py
@@ -1,21 +1,25 @@
###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
from . import common
-from . import test_search_parents
\ No newline at end of file
+from . import test_search_parents
+from . import test_attachment_migration
\ No newline at end of file
diff --git a/muk_utils/tests/common.py b/muk_utils/tests/common.py
index a7d9b7f..f78b254 100644
--- a/muk_utils/tests/common.py
+++ b/muk_utils/tests/common.py
@@ -1,170 +1,156 @@
-###################################################################################
-#
-# Copyright (C) 2017 MuK IT GmbH
-#
-# Odoo Proprietary License v1.0
-#
-# This software and associated files (the "Software") may only be used
-# (executed, modified, executed after modifications) if you have
-# purchased a valid license from the authors, typically via Odoo Apps,
-# or if you have received a written agreement from the authors of the
-# Software (see the COPYRIGHT file).
-#
-# You may develop Odoo modules that use the Software as a library
-# (typically by depending on it, importing it and using its resources),
-# but without copying any source code or material from the Software.
-# You may distribute those modules under the license of your choice,
-# provided that this license is compatible with the terms of the Odoo
-# Proprietary License (For example: LGPL, MIT, or proprietary licenses
-# similar to this one).
-#
-# It is forbidden to publish, distribute, sublicense, or sell copies of
-# the Software or modified copies of the Software.
-#
-# The above copyright notice and this permission notice must be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-#
-###################################################################################
-
-import os
-import time
-import hmac
-import hashlib
-import logging
-import functools
-import threading
-import traceback
-
-from odoo.tests import common, HOST, PORT
-
-_path = os.path.dirname(os.path.dirname(__file__))
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# Decorators
-#----------------------------------------------------------
-
-def multi_users(users=[['base.user_root', True], ['base.user_admin', True]], reset=True, raise_exception=True):
- def decorator(func):
- @functools.wraps(func)
- def wrapper(self, *args, **kwargs):
- user_list = users(self) if callable(users) else users
- test_results = []
- for user in user_list:
- self.cr.execute('SAVEPOINT test_multi_users')
- try:
- if not isinstance(user[0], int):
- self.uid = self.ref(user[0])
- else:
- self.uid = user[0]
- func(self, *args, **kwargs)
- except Exception as error:
- test_results.append({
- 'user': user[0],
- 'expect': user[1],
- 'result': False,
- 'error': error,
- })
- else:
- test_results.append({
- 'user': user[0],
- 'expect': user[1],
- 'result': True,
- 'error': None,
- })
- if reset:
- self.env.cache.invalidate()
- self.registry.clear_caches()
- self.registry.reset_changes()
- self.cr.execute('ROLLBACK TO SAVEPOINT test_multi_users')
- else:
- self._cr.execute('RELEASE SAVEPOINT test_multi_users')
- test_fails = []
- for result in test_results:
- if result['expect'] != result['result']:
- message = "Test (%s) with user (%s) failed!"
- _logger.info(message % (func.__name__, result['user']))
- if result['error']:
- _logger.error(result['error'], exc_info=True)
- test_fails.append(result)
- if test_fails:
- message = "%s out of %s tests failed" % (len(test_fails), len(test_results))
- if raise_exception:
- raise test_fails[0]['error']
- else:
- _logger.info(message)
- return test_results
- return wrapper
- return decorator
-
-def track_function(max_query_count=None, max_query_time=None, max_time=None, return_tracking=False):
- def decorator(func):
- @functools.wraps(func)
- def wrapper(*args, **kwargs):
- tracking_parameters = [func.__name__]
- threading.current_thread().query_time = 0
- threading.current_thread().query_count = 0
- threading.current_thread().perf_t0 = time.time()
- result = func(*args, **kwargs)
- message = "%s" % func.__name__
- if args and hasattr(args[0], "uid"):
- message = " (%s)" % args[0].uid
- if hasattr(threading.current_thread(), "query_count"):
- query_count = threading.current_thread().query_count
- query_time = threading.current_thread().query_time
- perf_t0 = threading.current_thread().perf_t0
- remaining_time = time.time() - perf_t0 - query_time
- time_taken = query_time + remaining_time
- message += " - %s Q %.3fs QT %.3fs OT %.3fs TT" % (
- query_count, query_time, remaining_time, time_taken
- )
- tracking_parameters += [
- query_count, query_time, remaining_time, time_taken
- ]
- if max_query_count and query_count > max_query_count:
- raise AssertionError("More than %s queries" % max_query_count)
- if max_query_time and query_time > max_query_time:
- raise AssertionError("Queries took longer than %.3fs" % max_query_time)
- if max_time and time_taken > max_time:
- raise AssertionError("Function took longer than %.3fs" % max_time)
- if not return_tracking:
- _logger.info(message)
- if return_tracking:
- return result, tracking_parameters
- return result
- return wrapper
- return decorator
-
-#----------------------------------------------------------
-# Test Cases
-#----------------------------------------------------------
-
-class HttpCase(common.HttpCase):
-
- def csrf_token(self, time_limit=3600):
- token = self.session.sid
- max_ts = '' if not time_limit else int(time.time() + time_limit)
- msg = '%s%s' % (token, max_ts)
- secret = self.env['ir.config_parameter'].sudo().get_param('database.secret')
- assert secret, "CSRF protection requires a configured database secret"
- hm = hmac.new(secret.encode('ascii'), msg.encode('utf-8'), hashlib.sha1).hexdigest()
- return '%so%s' % (hm, max_ts)
-
- def url_open(self, url, data=None, timeout=10, csrf=False):
- if url.startswith('/'):
- url = "http://%s:%s%s" % (HOST, PORT, url)
- if data:
- if csrf:
- data.update({'csrf_token': self.csrf_token()})
- return self.opener.post(url, data=data, timeout=timeout)
- return self.opener.get(url, timeout=timeout)
-
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import os
+import time
+import hmac
+import hashlib
+import logging
+import functools
+import threading
+import traceback
+
+from odoo.tests import common, HOST, PORT
+
+_path = os.path.dirname(os.path.dirname(__file__))
+_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# Decorators
+#----------------------------------------------------------
+
+def multi_users(users=[['base.user_root', True], ['base.user_admin', True]], reset=True, raise_exception=True):
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(self, *args, **kwargs):
+ user_list = users(self) if callable(users) else users
+ test_results = []
+ for user in user_list:
+ self.cr.execute('SAVEPOINT test_multi_users')
+ try:
+ if not isinstance(user[0], int):
+ self.uid = self.ref(user[0])
+ else:
+ self.uid = user[0]
+ func(self, *args, **kwargs)
+ except Exception as error:
+ test_results.append({
+ 'user': user[0],
+ 'expect': user[1],
+ 'result': False,
+ 'error': error,
+ })
+ else:
+ test_results.append({
+ 'user': user[0],
+ 'expect': user[1],
+ 'result': True,
+ 'error': None,
+ })
+ if reset:
+ self.env.cache.invalidate()
+ self.registry.clear_caches()
+ self.registry.reset_changes()
+ self.cr.execute('ROLLBACK TO SAVEPOINT test_multi_users')
+ else:
+ self._cr.execute('RELEASE SAVEPOINT test_multi_users')
+ test_fails = []
+ for result in test_results:
+ if result['expect'] != result['result']:
+ message = "Test (%s) with user (%s) failed!"
+ _logger.info(message % (func.__name__, result['user']))
+ if result['error']:
+ _logger.error(result['error'], exc_info=True)
+ test_fails.append(result)
+ if test_fails:
+ message = "%s out of %s tests failed" % (len(test_fails), len(test_results))
+ if raise_exception:
+ raise test_fails[0]['error']
+ else:
+ _logger.info(message)
+ return test_results
+ return wrapper
+ return decorator
+
+def track_function(max_query_count=None, max_query_time=None, max_time=None, return_tracking=False):
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ tracking_parameters = [func.__name__]
+ threading.current_thread().query_time = 0
+ threading.current_thread().query_count = 0
+ threading.current_thread().perf_t0 = time.time()
+ result = func(*args, **kwargs)
+ message = "%s" % func.__name__
+ if args and hasattr(args[0], "uid"):
+ message = " (%s)" % args[0].uid
+ if hasattr(threading.current_thread(), "query_count"):
+ query_count = threading.current_thread().query_count
+ query_time = threading.current_thread().query_time
+ perf_t0 = threading.current_thread().perf_t0
+ remaining_time = time.time() - perf_t0 - query_time
+ time_taken = query_time + remaining_time
+ message += " - %s Q %.3fs QT %.3fs OT %.3fs TT" % (
+ query_count, query_time, remaining_time, time_taken
+ )
+ tracking_parameters += [
+ query_count, query_time, remaining_time, time_taken
+ ]
+ if max_query_count and query_count > max_query_count:
+ raise AssertionError("More than %s queries" % max_query_count)
+ if max_query_time and query_time > max_query_time:
+ raise AssertionError("Queries took longer than %.3fs" % max_query_time)
+ if max_time and time_taken > max_time:
+ raise AssertionError("Function took longer than %.3fs" % max_time)
+ if not return_tracking:
+ _logger.info(message)
+ if return_tracking:
+ return result, tracking_parameters
+ return result
+ return wrapper
+ return decorator
+
+#----------------------------------------------------------
+# Test Cases
+#----------------------------------------------------------
+
+class HttpCase(common.HttpCase):
+
+ def csrf_token(self, time_limit=3600):
+ token = self.session.sid
+ max_ts = '' if not time_limit else int(time.time() + time_limit)
+ msg = '%s%s' % (token, max_ts)
+ secret = self.env['ir.config_parameter'].sudo().get_param('database.secret')
+ assert secret, "CSRF protection requires a configured database secret"
+ hm = hmac.new(secret.encode('ascii'), msg.encode('utf-8'), hashlib.sha1).hexdigest()
+ return '%so%s' % (hm, max_ts)
+
+ def url_open(self, url, data=None, timeout=10, csrf=False):
+ if url.startswith('/'):
+ url = "http://%s:%s%s" % (HOST, PORT, url)
+ if data:
+ if csrf:
+ data.update({'csrf_token': self.csrf_token()})
+ return self.opener.post(url, data=data, timeout=timeout)
+ return self.opener.get(url, timeout=timeout)
+
\ No newline at end of file
diff --git a/muk_utils/tests/test_attachment_migration.py b/muk_utils/tests/test_attachment_migration.py
index abe50ed..ebf5802 100644
--- a/muk_utils/tests/test_attachment_migration.py
+++ b/muk_utils/tests/test_attachment_migration.py
@@ -1,48 +1,51 @@
-###################################################################################
-#
-# Copyright (C) 2017 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import os
-import base64
-import logging
-
-from odoo import exceptions
-from odoo.tests import common
-
-_path = os.path.dirname(os.path.dirname(__file__))
-_logger = logging.getLogger(__name__)
-
-class MigrationTestCase(common.TransactionCase):
-
- def setUp(self):
- super(MigrationTestCase, self).setUp()
- self.model = self.env['ir.attachment']
- self.params = self.env['ir.config_parameter'].sudo()
- self.location = self.params.get_param('ir_attachment.location')
- if self.location == 'file':
- self.params.set_param('ir_attachment.location', 'db')
- else:
- self.params.set_param('ir_attachment.location', 'file')
-
- def tearDown(self):
- self.params.set_param('ir_attachment.location', self.location)
- super(MigrationTestCase, self).tearDown()
-
- def test_migration(self):
- self.model.search([], limit=5).action_migrate()
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import os
+import base64
+import logging
+
+from odoo import exceptions
+from odoo.tests import common
+
+_path = os.path.dirname(os.path.dirname(__file__))
+_logger = logging.getLogger(__name__)
+
+class MigrationTestCase(common.TransactionCase):
+
+ def setUp(self):
+ super(MigrationTestCase, self).setUp()
+ self.model = self.env['ir.attachment']
+ self.params = self.env['ir.config_parameter'].sudo()
+ self.location = self.params.get_param('ir_attachment.location')
+ if self.location == 'file':
+ self.params.set_param('ir_attachment.location', 'db')
+ else:
+ self.params.set_param('ir_attachment.location', 'file')
+
+ def tearDown(self):
+ self.params.set_param('ir_attachment.location', self.location)
+ super(MigrationTestCase, self).tearDown()
+
+ def test_migration(self):
+ self.model.search([], limit=25).with_context(migration_batch_size=5).action_migrate()
\ No newline at end of file
diff --git a/muk_utils/tests/test_search_parents.py b/muk_utils/tests/test_search_parents.py
index 3d4fed0..0f2ffca 100644
--- a/muk_utils/tests/test_search_parents.py
+++ b/muk_utils/tests/test_search_parents.py
@@ -1,62 +1,65 @@
-###################################################################################
-#
-# Copyright (C) 2017 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import os
-import base64
-import logging
-
-from odoo import exceptions
-from odoo.tests import common
-
-_path = os.path.dirname(os.path.dirname(__file__))
-_logger = logging.getLogger(__name__)
-
-class SearchParentTestCase(common.TransactionCase):
-
- def setUp(self):
- super(SearchParentTestCase, self).setUp()
- self.model = self.env['res.partner.category']
-
- def tearDown(self):
- super(SearchParentTestCase, self).tearDown()
-
- def _evaluate_parent_result(self, parents, records):
- for parent in parents:
- self.assertTrue(
- not parent.parent_id or
- parent.parent_id.id not in records.ids
- )
-
- def test_search_parents(self):
- records = self.model.search([])
- parents = self.model.search_parents([])
- self._evaluate_parent_result(parents, records)
-
- def test_search_parents_domain(self):
- records = self.model.search([('id', '!=', 1)])
- parents = self.model.search_parents([('id', '!=', 1)])
- self._evaluate_parent_result(parents, records)
-
- def test_search_read_parents(self):
- parents = self.model.search_parents([])
- read_names = parents.read(['name'])
- search_names = self.model.search_read_parents([], ['name'])
- self.assertTrue(read_names == search_names)
-
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import os
+import base64
+import logging
+
+from odoo import exceptions
+from odoo.tests import common
+
+_path = os.path.dirname(os.path.dirname(__file__))
+_logger = logging.getLogger(__name__)
+
+class SearchParentTestCase(common.TransactionCase):
+
+ def setUp(self):
+ super(SearchParentTestCase, self).setUp()
+ self.model = self.env['res.partner.category']
+
+ def tearDown(self):
+ super(SearchParentTestCase, self).tearDown()
+
+ def _evaluate_parent_result(self, parents, records):
+ for parent in parents:
+ self.assertTrue(
+ not parent.parent_id or
+ parent.parent_id.id not in records.ids
+ )
+
+ def test_search_parents(self):
+ records = self.model.search([])
+ parents = self.model.search_parents([])
+ self._evaluate_parent_result(parents, records)
+
+ def test_search_parents_domain(self):
+ records = self.model.search([('id', '!=', 1)])
+ parents = self.model.search_parents([('id', '!=', 1)])
+ self._evaluate_parent_result(parents, records)
+
+ def test_search_read_parents(self):
+ parents = self.model.search_parents([])
+ read_names = parents.read(['name'])
+ search_names = self.model.search_read_parents([], ['name'])
+ self.assertTrue(read_names == search_names)
+
\ No newline at end of file
diff --git a/muk_utils/tools/__init__.py b/muk_utils/tools/__init__.py
index 5940866..6631a99 100644
--- a/muk_utils/tools/__init__.py
+++ b/muk_utils/tools/__init__.py
@@ -1,19 +1,22 @@
###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
diff --git a/muk_utils/tools/cache.py b/muk_utils/tools/cache.py
index 224a405..0961c69 100644
--- a/muk_utils/tools/cache.py
+++ b/muk_utils/tools/cache.py
@@ -1,60 +1,63 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import time
-import logging
-import datetime
-import functools
-
-_logger = logging.getLogger(__name__)
-
-
-#----------------------------------------------------------
-# Properties
-#----------------------------------------------------------
-
-class cached_property(object):
-
- def __init__(self, timeout=None):
- self.timeout = timeout
-
- def __call__(self, func):
- return functools.update_wrapper(self, func)
-
- def __get__(self, obj, cls):
- if obj is None:
- return self
- try:
- value, last_updated = obj.__dict__[self.__name__]
- except KeyError:
- pass
- else:
- if self.timeout is None:
- return value
- elif self.timeout >= time.time() - last_updated:
- return value
- value = self.__wrapped__(obj)
- obj.__dict__[self.__name__] = (value, time.time())
- return value
-
- def __delete__(self, obj):
- obj.__dict__.pop(self.__name__, None)
-
- def __set__(self, obj, value):
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import time
+import logging
+import datetime
+import functools
+
+_logger = logging.getLogger(__name__)
+
+
+#----------------------------------------------------------
+# Properties
+#----------------------------------------------------------
+
+class cached_property(object):
+
+ def __init__(self, timeout=None):
+ self.timeout = timeout
+
+ def __call__(self, func):
+ return functools.update_wrapper(self, func)
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ try:
+ value, last_updated = obj.__dict__[self.__name__]
+ except KeyError:
+ pass
+ else:
+ if self.timeout is None:
+ return value
+ elif self.timeout >= time.time() - last_updated:
+ return value
+ value = self.__wrapped__(obj)
+ obj.__dict__[self.__name__] = (value, time.time())
+ return value
+
+ def __delete__(self, obj):
+ obj.__dict__.pop(self.__name__, None)
+
+ def __set__(self, obj, value):
obj.__dict__[self.__name__] = (value, time())
\ No newline at end of file
diff --git a/muk_utils/tools/file.py b/muk_utils/tools/file.py
index e71899b..ceeb529 100644
--- a/muk_utils/tools/file.py
+++ b/muk_utils/tools/file.py
@@ -1,116 +1,119 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import os
-import re
-import io
-import sys
-import base64
-import shutil
-import urllib
-import logging
-import hashlib
-import binascii
-import tempfile
-import mimetypes
-import unicodedata
-
-from odoo.tools import human_size
-from odoo.tools.mimetypes import guess_mimetype
-
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# File Helper
-#----------------------------------------------------------
-
-def slugify(value, lower=True):
- value = unicodedata.normalize('NFKD', value)
- value = value.encode('ascii', 'ignore').decode('ascii')
- value = value.lower() if lower else value
- value = re.sub('[^\w\s-]', '', value)
- value = re.sub('[-\s]+', '-', value)
- return value.strip()
-
-def check_name(name):
- tmp_dir = tempfile.mkdtemp()
- try:
- open(os.path.join(tmp_dir, name), 'a').close()
- except IOError:
- return False
- finally:
- shutil.rmtree(tmp_dir)
- return True
-
-def compute_name(name, suffix, escape_suffix):
- if escape_suffix:
- name, extension = os.path.splitext(name)
- return "%s(%s)%s" % (name, suffix, extension)
- else:
- return "%s(%s)" % (name, suffix)
-
-def unique_name(name, names, escape_suffix=False):
- if not name in names:
- return name
- else:
- suffix = 1
- name = compute_name(name, suffix, escape_suffix)
- while name in names:
- suffix += 1
- name = compute_name(name, suffix, escape_suffix)
- return name
-
-def unique_files(files):
- ufiles = []
- unames = []
- for file in files:
- uname = unique_name(file[0], unames, escape_suffix=True)
- ufiles.append((uname, file[1]))
- unames.append(uname)
- return ufiles
-
-def guess_extension(filename=None, mimetype=None, binary=None):
- extension = filename and os.path.splitext(filename)[1][1:].strip().lower()
- if not extension and mimetype:
- extension = mimetypes.guess_extension(mimetype)[1:].strip().lower()
- if not extension and binary:
- mimetype = guess_mimetype(binary, default="")
- extension = mimetypes.guess_extension(mimetype)[1:].strip().lower()
- return extension
-
-#----------------------------------------------------------
-# System Helper
-#----------------------------------------------------------
-
-def ensure_path_directories(path):
- directory_path = os.path.dirname(path)
- if not os.path.exists(directory_path):
- os.makedirs(directory_path)
-
-def remove_empty_directories(path):
- if not os.path.isdir(path):
- return
- entries = os.listdir(path)
- if len(entries) > 0:
- for entry in entries:
- subpath = os.path.join(path, entry)
- if os.path.isdir(subpath):
- self._remove_empty_directories(subpath)
- else:
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import os
+import re
+import io
+import sys
+import base64
+import shutil
+import urllib
+import logging
+import hashlib
+import binascii
+import tempfile
+import mimetypes
+import unicodedata
+
+from odoo.tools import human_size
+from odoo.tools.mimetypes import guess_mimetype
+
+_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# File Helper
+#----------------------------------------------------------
+
+def slugify(value, lower=True):
+ value = unicodedata.normalize('NFKD', value)
+ value = value.encode('ascii', 'ignore').decode('ascii')
+ value = value.lower() if lower else value
+ value = re.sub('[^\w\s-]', '', value)
+ value = re.sub('[-\s]+', '-', value)
+ return value.strip()
+
+def check_name(name):
+ tmp_dir = tempfile.mkdtemp()
+ try:
+ open(os.path.join(tmp_dir, name), 'a').close()
+ except IOError:
+ return False
+ finally:
+ shutil.rmtree(tmp_dir)
+ return True
+
+def compute_name(name, suffix, escape_suffix):
+ if escape_suffix:
+ name, extension = os.path.splitext(name)
+ return "%s(%s)%s" % (name, suffix, extension)
+ else:
+ return "%s(%s)" % (name, suffix)
+
+def unique_name(name, names, escape_suffix=False):
+ if not name in names:
+ return name
+ else:
+ suffix = 1
+ name = compute_name(name, suffix, escape_suffix)
+ while name in names:
+ suffix += 1
+ name = compute_name(name, suffix, escape_suffix)
+ return name
+
+def unique_files(files):
+ ufiles = []
+ unames = []
+ for file in files:
+ uname = unique_name(file[0], unames, escape_suffix=True)
+ ufiles.append((uname, file[1]))
+ unames.append(uname)
+ return ufiles
+
+def guess_extension(filename=None, mimetype=None, binary=None):
+ extension = filename and os.path.splitext(filename)[1][1:].strip().lower()
+ if not extension and mimetype:
+ extension = mimetypes.guess_extension(mimetype)[1:].strip().lower()
+ if not extension and binary:
+ mimetype = guess_mimetype(binary, default="")
+ extension = mimetypes.guess_extension(mimetype)[1:].strip().lower()
+ return extension
+
+#----------------------------------------------------------
+# System Helper
+#----------------------------------------------------------
+
+def ensure_path_directories(path):
+ directory_path = os.path.dirname(path)
+ if not os.path.exists(directory_path):
+ os.makedirs(directory_path)
+
+def remove_empty_directories(path):
+ if not os.path.isdir(path):
+ return
+ entries = os.listdir(path)
+ if len(entries) > 0:
+ for entry in entries:
+ subpath = os.path.join(path, entry)
+ if os.path.isdir(subpath):
+ self._remove_empty_directories(subpath)
+ else:
os.rmdir(path)
\ No newline at end of file
diff --git a/muk_utils/tools/http.py b/muk_utils/tools/http.py
index 6504569..b12e6cb 100644
--- a/muk_utils/tools/http.py
+++ b/muk_utils/tools/http.py
@@ -1,56 +1,59 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import urllib
-import base64
-import logging
-
-from werkzeug.datastructures import CombinedMultiDict
-
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# Header Helper
-#----------------------------------------------------------
-
-def decode_http_basic_authentication_value(value):
- try:
- username, password = base64.b64decode(value).decode().split(':', 1)
- return urllib.parse.unquote(username), urllib.parse.unquote(password)
- except:
- return None, None
-
-def decode_http_basic_authentication(encoded_header):
- header_values = encoded_header.strip().split(' ')
- if len(header_values) == 1:
- return decode_http_basic_authentication_value(header_values[0])
- if len(header_values) == 2 and header_values[0].strip().lower() == 'basic':
- return decode_http_basic_authentication_value(header_values[1])
- return None, None
-
-#----------------------------------------------------------
-# Werkzeug Helper
-#----------------------------------------------------------
-
-def request_params(httprequest):
- return CombinedMultiDict([
- httprequest.args,
- httprequest.form,
- httprequest.files
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import urllib
+import base64
+import logging
+
+from werkzeug.datastructures import CombinedMultiDict
+
+_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# Header Helper
+#----------------------------------------------------------
+
+def decode_http_basic_authentication_value(value):
+ try:
+ username, password = base64.b64decode(value).decode().split(':', 1)
+ return urllib.parse.unquote(username), urllib.parse.unquote(password)
+ except:
+ return None, None
+
+def decode_http_basic_authentication(encoded_header):
+ header_values = encoded_header.strip().split(' ')
+ if len(header_values) == 1:
+ return decode_http_basic_authentication_value(header_values[0])
+ if len(header_values) == 2 and header_values[0].strip().lower() == 'basic':
+ return decode_http_basic_authentication_value(header_values[1])
+ return None, None
+
+#----------------------------------------------------------
+# Werkzeug Helper
+#----------------------------------------------------------
+
+def request_params(httprequest):
+ return CombinedMultiDict([
+ httprequest.args,
+ httprequest.form,
+ httprequest.files
])
\ No newline at end of file
diff --git a/muk_utils/tools/json.py b/muk_utils/tools/json.py
index a66905b..183a3e1 100644
--- a/muk_utils/tools/json.py
+++ b/muk_utils/tools/json.py
@@ -1,19 +1,22 @@
###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
#
# 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.
+# it under the terms of the GNU Lesser 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.
+# GNU Lesser 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 .
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
#
###################################################################################
diff --git a/muk_utils/tools/patch.py b/muk_utils/tools/patch.py
index c6e72a2..c1a66d0 100644
--- a/muk_utils/tools/patch.py
+++ b/muk_utils/tools/patch.py
@@ -1,36 +1,39 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import logging
-
-from odoo import api
-
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# Patch Helper
-#----------------------------------------------------------
-
-def monkey_patch(cls):
- def decorate(func):
- name = func.__name__
- func.super = getattr(cls, name, None)
- setattr(cls, name, func)
- return func
- return decorate
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import logging
+
+from odoo import api
+
+_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# Patch Helper
+#----------------------------------------------------------
+
+def monkey_patch(cls):
+ def decorate(func):
+ name = func.__name__
+ func.super = getattr(cls, name, None)
+ setattr(cls, name, func)
+ return func
+ return decorate
diff --git a/muk_utils/tools/rst.py b/muk_utils/tools/rst.py
index b5ba4d6..8bdf2cc 100644
--- a/muk_utils/tools/rst.py
+++ b/muk_utils/tools/rst.py
@@ -1,52 +1,55 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import logging
-
-from docutils import nodes
-from docutils.core import publish_string
-from docutils.transforms import Transform, writer_aux
-from docutils.writers.html4css1 import Writer
-
-from odoo import tools
-
-_logger = logging.getLogger(__name__)
-
-class ReStructuredTextFilterMessages(Transform):
- default_priority = 870
- def apply(self):
- for node in self.document.traverse(nodes.system_message):
- node.parent.remove(node)
-
-class ReStructuredTextWriter(Writer):
- def get_transforms(self):
- return [ReStructuredTextFilterMessages, writer_aux.Admonitions]
-
-def rst2html(content):
- overrides = {
- 'embed_stylesheet': False,
- 'doctitle_xform': False,
- 'output_encoding': 'unicode',
- 'xml_declaration': False,
- }
- output = publish_string(content,
- settings_overrides=overrides,
- writer=ReStructuredTextWriter()
- )
- return tools.html_sanitize(output)
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import logging
+
+from docutils import nodes
+from docutils.core import publish_string
+from docutils.transforms import Transform, writer_aux
+from docutils.writers.html4css1 import Writer
+
+from odoo import tools
+
+_logger = logging.getLogger(__name__)
+
+class ReStructuredTextFilterMessages(Transform):
+ default_priority = 870
+ def apply(self):
+ for node in self.document.traverse(nodes.system_message):
+ node.parent.remove(node)
+
+class ReStructuredTextWriter(Writer):
+ def get_transforms(self):
+ return [ReStructuredTextFilterMessages, writer_aux.Admonitions]
+
+def rst2html(content):
+ overrides = {
+ 'embed_stylesheet': False,
+ 'doctitle_xform': False,
+ 'output_encoding': 'unicode',
+ 'xml_declaration': False,
+ }
+ output = publish_string(content,
+ settings_overrides=overrides,
+ writer=ReStructuredTextWriter()
+ )
+ return tools.html_sanitize(output)
diff --git a/muk_utils/tools/security.py b/muk_utils/tools/security.py
index d4dec15..ef008a5 100644
--- a/muk_utils/tools/security.py
+++ b/muk_utils/tools/security.py
@@ -1,34 +1,37 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import string
-import random
-import logging
-
-_logger = logging.getLogger(__name__)
-
-UNICODE_ASCII_CHARACTERS = string.ascii_letters + string.digits
-
-#----------------------------------------------------------
-# Generator
-#----------------------------------------------------------
-
-def generate_token(length=30, chars=UNICODE_ASCII_CHARACTERS):
- generator = random.SystemRandom()
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import string
+import random
+import logging
+
+_logger = logging.getLogger(__name__)
+
+UNICODE_ASCII_CHARACTERS = string.ascii_letters + string.digits
+
+#----------------------------------------------------------
+# Generator
+#----------------------------------------------------------
+
+def generate_token(length=30, chars=UNICODE_ASCII_CHARACTERS):
+ generator = random.SystemRandom()
return "".join(generator.choice(chars) for index in range(length))
\ No newline at end of file
diff --git a/muk_utils/tools/types.py b/muk_utils/tools/types.py
index f75bafc..11ef779 100644
--- a/muk_utils/tools/types.py
+++ b/muk_utils/tools/types.py
@@ -1,37 +1,40 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import logging
-
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# Meta Classes
-#----------------------------------------------------------
-
-class Singleton(type):
-
- _instances = {}
-
- def __call__(cls, *args, **kwargs):
- if cls not in cls._instances:
- cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
- return cls._instances[cls]
-
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import logging
+
+_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# Meta Classes
+#----------------------------------------------------------
+
+class Singleton(type):
+
+ _instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls._instances:
+ cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
+ return cls._instances[cls]
+
\ No newline at end of file
diff --git a/muk_utils/tools/utils.py b/muk_utils/tools/utils.py
index 0037591..84e3b4c 100644
--- a/muk_utils/tools/utils.py
+++ b/muk_utils/tools/utils.py
@@ -1,45 +1,48 @@
-###################################################################################
-#
-# Copyright (C) 2018 MuK IT GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-###################################################################################
-
-import logging
-
-_logger = logging.getLogger(__name__)
-
-#----------------------------------------------------------
-# List Methods
-#----------------------------------------------------------
-
-def uniquify_list(seq):
- seen = set()
- return [
- val for val in seq if val not in seen and not seen.add(val)
- ]
-
-#----------------------------------------------------------
-# Safe Execute
-#----------------------------------------------------------
-
-def safe_execute_exception(default, exception, function, *args, **kwargs):
- try:
- return function(*args, **kwargs)
- except exception:
- return default
-
-def safe_execute(default, function, *args, **kwargs):
+###################################################################################
+#
+# Copyright (c) 2017-2019 MuK IT GmbH.
+#
+# This file is part of MuK Utils
+# (see https://mukit.at).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+#
+###################################################################################
+
+import logging
+
+_logger = logging.getLogger(__name__)
+
+#----------------------------------------------------------
+# List Methods
+#----------------------------------------------------------
+
+def uniquify_list(seq):
+ seen = set()
+ return [
+ val for val in seq if val not in seen and not seen.add(val)
+ ]
+
+#----------------------------------------------------------
+# Safe Execute
+#----------------------------------------------------------
+
+def safe_execute_exception(default, exception, function, *args, **kwargs):
+ try:
+ return function(*args, **kwargs)
+ except exception:
+ return default
+
+def safe_execute(default, function, *args, **kwargs):
return safe_execute_exception(default, Exception, function, *args, **kwargs)
\ No newline at end of file
diff --git a/muk_utils/views/ir_attachment.xml b/muk_utils/views/ir_attachment.xml
index c259a05..f517990 100644
--- a/muk_utils/views/ir_attachment.xml
+++ b/muk_utils/views/ir_attachment.xml
@@ -1,21 +1,26 @@
-
+ -->
diff --git a/muk_utils/views/mixins_groups.xml b/muk_utils/views/mixins_groups.xml
index 54bd963..0f9bdcc 100644
--- a/muk_utils/views/mixins_groups.xml
+++ b/muk_utils/views/mixins_groups.xml
@@ -1,21 +1,26 @@
-
+ -->
diff --git a/muk_utils/views/res_config_settings.xml b/muk_utils/views/res_config_settings.xml
index 8e4ec91..7f8a0ff 100644
--- a/muk_utils/views/res_config_settings.xml
+++ b/muk_utils/views/res_config_settings.xml
@@ -1,56 +1,61 @@
-
-
-
-
-
-
-
- res.config.settings.view.form
- res.config.settings
-
-
-
-
Storage
-
-
-
-
-
-
- Attachment storage location
-
-
-
-
-
-
- Save this page before triggering the migration.
-