From 102b015a8b24c5a87f5b72fb244f78bea90d7e5a Mon Sep 17 00:00:00 2001 From: benjaoming Date: Sun, 18 May 2014 13:21:52 +0200 Subject: [PATCH] south migration and django 1.7 transitional support, remove django_notify and use django_nyt --- MANIFEST | 1 - django_notify/COPYING | 674 ------------------ django_notify/README.md | 129 ---- django_notify/__init__.py | 70 -- django_notify/admin.py | 10 - django_notify/decorators.py | 44 -- .../docs/misc/screenshot_dropdown.png | Bin 17111 -> 0 bytes django_notify/locale/de/LC_MESSAGES/django.mo | Bin 2213 -> 0 bytes django_notify/locale/de/LC_MESSAGES/django.po | 136 ---- django_notify/locale/ru/LC_MESSAGES/django.mo | Bin 2614 -> 0 bytes django_notify/locale/ru/LC_MESSAGES/django.po | 137 ---- django_notify/management/__init__.py | 0 django_notify/management/commands/__init__.py | 0 .../management/commands/notifymail.py | 159 ----- django_notify/migrations/0001_initial.py | 144 ---- ...uto__add_field_notification_occurrences.py | 103 --- ...003_auto__add_field_subscription_latest.py | 104 --- .../0004_auto__chg_field_notification_url.py | 102 --- django_notify/migrations/__init__.py | 0 django_notify/models.py | 173 ----- django_notify/settings.py | 66 -- .../emails/notification_email_message.txt | 13 - .../emails/notification_email_subject.txt | 2 - django_notify/tests.py | 16 - django_notify/urls.py | 18 - django_notify/views.py | 65 -- docs/installation.rst | 6 +- requirements.txt | 5 +- runtests.py | 2 +- setup.py | 42 +- testproject/django_notify | 1 - ...ated-customauthuser.db => prepopulated.db} | Bin 130048 -> 128000 bytes testproject/testproject/db/prepopulated.db | Bin 264192 -> 0 bytes testproject/testproject/settings/__init__.py | 15 +- testproject/testproject/urls.py | 2 +- wiki/__init__.py | 2 +- wiki/core/compat.py | 2 +- wiki/models/__init__.py | 4 +- .../plugins/images/migrations/0001_initial.py | 110 +-- wiki/plugins/notifications/forms.py | 4 +- wiki/plugins/notifications/models.py | 4 +- wiki/plugins/notifications/settings.py | 2 +- .../plugins/notifications/menubaritem.html | 6 +- wiki/templates/wiki/accounts/login.html | 37 +- wiki/templates/wiki/root_missing.html | 39 +- wiki/tests/testdata/urls.py | 2 +- 46 files changed, 119 insertions(+), 2332 deletions(-) delete mode 100644 django_notify/COPYING delete mode 100644 django_notify/README.md delete mode 100644 django_notify/__init__.py delete mode 100644 django_notify/admin.py delete mode 100644 django_notify/decorators.py delete mode 100644 django_notify/docs/misc/screenshot_dropdown.png delete mode 100644 django_notify/locale/de/LC_MESSAGES/django.mo delete mode 100644 django_notify/locale/de/LC_MESSAGES/django.po delete mode 100644 django_notify/locale/ru/LC_MESSAGES/django.mo delete mode 100644 django_notify/locale/ru/LC_MESSAGES/django.po delete mode 100644 django_notify/management/__init__.py delete mode 100644 django_notify/management/commands/__init__.py delete mode 100644 django_notify/management/commands/notifymail.py delete mode 100644 django_notify/migrations/0001_initial.py delete mode 100644 django_notify/migrations/0002_auto__add_field_notification_occurrences.py delete mode 100644 django_notify/migrations/0003_auto__add_field_subscription_latest.py delete mode 100644 django_notify/migrations/0004_auto__chg_field_notification_url.py delete mode 100644 django_notify/migrations/__init__.py delete mode 100644 django_notify/models.py delete mode 100644 django_notify/settings.py delete mode 100644 django_notify/templates/emails/notification_email_message.txt delete mode 100644 django_notify/templates/emails/notification_email_subject.txt delete mode 100644 django_notify/tests.py delete mode 100644 django_notify/urls.py delete mode 100644 django_notify/views.py delete mode 120000 testproject/django_notify rename testproject/{testproject/db/prepopulated-customauthuser.db => prepopulated.db} (73%) delete mode 100644 testproject/testproject/db/prepopulated.db diff --git a/MANIFEST b/MANIFEST index 780f94e88..0a1a3b680 100644 --- a/MANIFEST +++ b/MANIFEST @@ -5,4 +5,3 @@ include setup.cfg include requirements.txt include model_chart_wiki.pdf recursive-include wiki *.html *.txt *.png *.js *.css *.gif *.less *.mo *.po *.otf *.svg *.woff *.eot *.ttf -recursive-include django_notify *.html *.txt *.png *.js *.css *.gif *.mo *.po diff --git a/django_notify/COPYING b/django_notify/COPYING deleted file mode 100644 index ee882461e..000000000 --- a/django_notify/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - django-wiki Copyright (C) 2012 Benjamin Bach - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/django_notify/README.md b/django_notify/README.md deleted file mode 100644 index b98720ad5..000000000 --- a/django_notify/README.md +++ /dev/null @@ -1,129 +0,0 @@ -django_notify -============= - -NB! This application is not the same as the PyPi project by the very same name, -nor is an integral part of django-wiki, it's a standalone project. -That PyPi project by the same name has been long dead (but seemed nice), -and we will try to get the keys for their PyPi repo or something... but meanwhile! - -django_notify does this: - -```python -from django_notify import notify - -EVENT_KEY = "my_key" -notify(_("OMG! Something happened"), EVENT_KEY) -``` - -All users subscribing to `EVENT_KEY` will have a notification created in their -stack. If you have emails enabled, they may get a summary of notifications at an -interval of their choice. - -Why should you do this? ------------------------ - -Users need an cleverly sifted stream of events. Ensure that they have the -possibility of customizing what they deem fit for their stream. - -What do you need to do? ------------------------ - -You need to do a lot. Firstly, you need to write some javascript that will -fetch the latest notifications and display them in some area of the screen. -Upon clicking that icon, the latest notifications are displayed. Something like -this: - -![Javascript drop-down](./docs/misc/screenshot_dropdown.png) - -Here is a snippet example to get you started, but you need to get ui.js from [django-wiki/plugins/notifications](https://github.com/benjaoming/django-wiki/tree/master/wiki/plugins/notifications/static/wiki/plugins/notifications/js) -which is a couple of utility functions that use JQuery to get notifications from -a JSON view and display them in the right DOM element. - -```html -

Notifications:

- - - -``` - -Usage ------ - - -### Adding a notification - -```python - from django_notify import notify - - EVENT_KEY = "my_key" - notify(_("OMG! Something happened"), EVENT_KEY) -``` - -### Adding a notification with a certain target object - -The Notification model has a GenericForeignKey which can link it to any other -object. This is nice, because you might have an intention to go the other way -around and ask "for this object, are there any notifications?" - -```python - notify(_("OMG! Something happened"), EVENT_KEY, target_object=my_model_instance) -``` - -### Excluding certain recepients - -By setting the kwarg `filter_exclude` to a dictionary of lookup fields for -`models.Subscription`, you may exclude certain users from getting a notification. -For instance, if a notification is solely for staff members: - -```python - notify( - _("OMG! Something happened"), EVENT_KEY, - filter_exclude={'settings__user__is_staff': True} - ) -``` - -### Disabling notifications - -Use `decorators.disable_notify` to ensure that all notifications within a function are disabled. - -For instance: - -```python - from django_notify.decorators import disable_notify - @disable_notify - def my_view(request): - ... -``` - -*This is a work in progress* ----------------------------- - -This application should live outside django-wiki. -It will! But only as soon as there's time to package it separately... - -TODO: - - * Missing email functionality - * Functions to easily retrive notifications and mark them as read - * Some easy-to-use template tags and templates to override. - * Examples of how to extend diff --git a/django_notify/__init__.py b/django_notify/__init__.py deleted file mode 100644 index 8fbb66ee0..000000000 --- a/django_notify/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -# This package and all its sub-packages are part of django_notify, -# except where otherwise stated. -# -# django_notify is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# django_notify is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with django_notify. If not, see . - -# Unused feature, atm. everything is bundled with django-wiki -from __future__ import unicode_literals - -VERSION = "0.0.4" - -from django.contrib.contenttypes.models import ContentType -from django.db.models import Model -from django.utils.translation import ugettext as _ - -from . import models - -_disable_notifications = False - -def notify(message, key, target_object=None, url=None, filter_exclude={}): - """ - Notify subscribing users of a new event. Key can be any kind of string, - just make sure to reuse it where applicable! Object_id is some identifier - of an object, for instance if a user subscribes to a specific comment thread, - you could write: - - notify("there was a response to your comment", "comment_response", - target_object=PostersObject, - url=reverse('comments:view', args=(PostersObject.id,))) - - The below example notifies everyone subscribing to the "new_comments" key - with the message "New comment posted". - - notify("New comment posted", "new_comments") - - filter_exclude: a dictionary to exclude special elements of subscriptions - in the queryset, for instance filter_exclude={''} - - """ - - if _disable_notifications: - return 0 - - if target_object: - if not isinstance(target_object, Model): - raise TypeError(_("You supplied a target_object that's not an instance of a django Model.")) - object_id = target_object.id - else: - object_id = None - - objects = models.Notification.create_notifications( - key, - object_id=object_id, - message=message, - url=url, - filter_exclude=filter_exclude, - ) - return len(objects) - diff --git a/django_notify/admin.py b/django_notify/admin.py deleted file mode 100644 index 8929a681c..000000000 --- a/django_notify/admin.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.contrib import admin - -from django_notify import models -from django_notify import settings - -if settings.ENABLE_ADMIN: - admin.site.register(models.NotificationType) - admin.site.register(models.Notification) - admin.site.register(models.Settings) - admin.site.register(models.Subscription) \ No newline at end of file diff --git a/django_notify/decorators.py b/django_notify/decorators.py deleted file mode 100644 index f1d5771d2..000000000 --- a/django_notify/decorators.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -from django.utils import simplejson as json -from django.http import HttpResponse -from django.contrib.auth.decorators import login_required - -import django_notify - -def disable_notify(func): - """Disable notifications. Example: - - @disable_notify - def your_function(): - notify("no one will be notified", ...) - """ - def wrap(request, *args, **kwargs): - django_notify._disable_notifications = True - response = func(request, *args, **kwargs) - django_notify._disable_notifications = False - return response - return wrap - -def login_required_ajax(func): - """Similar to login_required. But if the request is an ajax request, then - it returns an error in json with a 403 status code.""" - - def wrap(request, *args, **kwargs): - if request.is_ajax(): - if not request.user or not request.user.is_authenticated(): - return json_view(lambda *a, **kw: {'error': 'not logged in'})(request, status=403) - return func(request, *args, **kwargs) - else: - return login_required(func)(request, *args, **kwargs) - return wrap - -def json_view(func): - def wrap(request, *args, **kwargs): - obj = func(request, *args, **kwargs) - data = json.dumps(obj, ensure_ascii=False) - status = kwargs.get('status', 200) - response = HttpResponse(mimetype='application/json', status=status) - response.write(data) - return response - return wrap - diff --git a/django_notify/docs/misc/screenshot_dropdown.png b/django_notify/docs/misc/screenshot_dropdown.png deleted file mode 100644 index 0c2eb7935a7eb9a50f97e9f0c7f632d84d4483aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17111 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 UAoDdyJpcdz07*qoM6N<$f+;fqCjbBd diff --git a/django_notify/locale/de/LC_MESSAGES/django.mo b/django_notify/locale/de/LC_MESSAGES/django.mo deleted file mode 100644 index 29fa388a492ae14258c84fc3a2728f2d171f89c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2213 zcmai!O^g&p6vqq2ukjQ7{)&$!L^jatEGTXU*9Z(BYj$Dd4iLSVn&~$^#dcTCR#or1 zbK``G=S>VTkpnmH#6%JgbM=ZR!&Ofnym;~8|Mkww48%~G?%&kctN(kiUfj zVA7r`_IP}Uo!HU!t{B^PAf;#u4ywu8W4sLPtuA2luv+L=rUJS&ZiF^;psw+pQc>p7 z^mCjkMrgyt9+r}^k$u6b(Q$HXP>ZbBMWPAR&b3{o=qQx!(D9~?aYx#2(yFm3xh}c( z74Af}!Z8|~;ZO&SL9H0uVbA%dTG`&&muSdrL#A6j*xg=S2J9nOdXhNpQ=hw%dLqE` z#)MCynhIsVpBwn4DQcf_{Bm0X^y>I-kR8wm_d?W=AQCmDSG3I>ExDBQ8I@`SUE3=n zh(N6CT;&F?iv00Fs{ic-)v(MElsM4CXl4+GU)jIpO z!?loPPDJ+M*6IX?bl!uA}6l)pG@|%A$*M9kjLdQOgzg zx}RjuWjPlJ*-Xb3AH#ypN}X}iVqM_qoqmJPRt9QI3rnZ(sh*vw*G@Tv*y3FH-=N4} z#rOKTHugAmdfFp$^A}rZlDT{9L|>PEa@uxWirdkkxweV5<-GJdPtG`Bga+Au)eZAU zj??zv`Gc1B&C~4k@tUHOY;~vSO{W)4R@#g$t>XgjR3jZ6D_Eo;uuVBmqezwFzh`wqmUDs3N#v@sZK7)VFr_$$(vnJxPZ84!q~QLRaS->Zrc^J) zTE_i;Fifh6+m><0ZH6;b6D2%VdAZ64GYeJy4oEtInq&2g4{03U%yNWI1&4Z#YGJ2@ z7o~&;_>|m#uzKhKf^u$nv+SA-0^($V#zHan7hkc>zC73pnJJf&5Kk+eUOv2yOynx` TJxj7u44<#+dw5Iwu~Ppp#PeQG diff --git a/django_notify/locale/de/LC_MESSAGES/django.po b/django_notify/locale/de/LC_MESSAGES/django.po deleted file mode 100644 index 132fdb550..000000000 --- a/django_notify/locale/de/LC_MESSAGES/django.po +++ /dev/null @@ -1,136 +0,0 @@ -# THE GERMAN TRANSLATION OF DJANGO-NOTIFY. -# Copyright (C) 2013 THOMAS LOTTERMANN -# This file is distributed under the same license as the DJANGO-WIKI package. -# THOMAS LOTTERMANN , 2013. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: 0.18\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-06 20:37+0200\n" -"Last-Translator: Thomas Lottermann \n" -"Language-Team: \n" -"Language: German\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: __init__.py:52 -msgid "You supplied a target_object that's not an instance of a django Model." -msgstr "Das gegebene \"target_object\" ist keine Instanz eines Django Models." - -#: models.py:14 -msgid "unique key" -msgstr "eindeutiger Schlüssel" - -#: models.py:16 -msgid "verbose name" -msgstr "sprechender Name" - -#: models.py:25 -msgid "type" -msgstr "Typ" - -#: models.py:26 -msgid "types" -msgstr "Typen" - -#: models.py:31 -msgid "interval" -msgstr "Interval" - -#: models.py:35 -#, python-format -msgid "Settings for %s" -msgstr "Einstellungen für %s" - -#: models.py:40 models.py:41 -msgid "settings" -msgstr "Einstellungen" - -#: models.py:48 -msgid "Leave this blank to subscribe to any kind of object" -msgstr "Leerlassen um beliebiges Object zu abonnieren" - -#: models.py:53 -#, python-format -msgid "Subscription for: %s" -msgstr "Abonnement von: %s" - -#: models.py:58 -msgid "subscription" -msgstr "Abonnement" - -#: models.py:59 -msgid "subscriptions" -msgstr "Abonnements" - -#: models.py:65 -msgid "link for notification" -msgstr "Link zur Benachrichtigung" - -#: models.py:71 -msgid "occurrences" -msgstr "Häufigkeit" - -#: models.py:72 -msgid "" -"If the same notification was fired multiple times with no intermediate " -"notifications" -msgstr "" -"Wenn die gleiche Benachrichtigung öfter aufgetreten ist ohne " -"zwischenzeitiges senden" - -#: models.py:125 -msgid "notification" -msgstr "Benachrichtigung" - -#: models.py:126 -msgid "notifications" -msgstr "Benachrichtigungen" - -#: settings.py:19 -msgid "You have new notifications" -msgstr "Du hast neue Benachrichtigungen" - -#: settings.py:38 -msgid "instant" -msgstr "sofort" - -#: settings.py:39 -msgid "daily" -msgstr "täglich" - -#: settings.py:40 -msgid "weekly" -msgstr "wöchentlich" - -#: views.py:32 -#, python-format -msgid "%d times" -msgstr "%d mal" - -#: templates/emails/notification_email_message.txt:2 -#, python-format -msgid "Dear %(username)s," -msgstr "Hallo %(username)s," - -#: templates/emails/notification_email_message.txt:4 -#, python-format -msgid " These are the %(digest)s notifications from %(site)s." -msgstr " Dies sind die %(digest)s versendeten Benachrichtigungen von %(site)s." - -#: templates/emails/notification_email_message.txt:10 -msgid "Thanks for using our site!" -msgstr "Danke, dass du unsere Seite nutzt!" - -#: templates/emails/notification_email_message.txt:12 -msgid "Sincerely" -msgstr "Viele Grüße" - -#: templates/emails/notification_email_subject.txt:2 -#, python-format -msgid " %(digest)s Notifications %(site)s." -msgstr " Benachrichtigungen von %(site)s (%(digest)s versendet)." diff --git a/django_notify/locale/ru/LC_MESSAGES/django.mo b/django_notify/locale/ru/LC_MESSAGES/django.mo deleted file mode 100644 index 3ad3d260a4260c05ed157058f631500653322a42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2614 zcmZ{k+iw(A9LJ9es4L#^hKlirq(}o(XIdI@%Yu=MqErc8jXs&#-Lt!g?#?W8&a^i2 z&=v_q3iZJn6C;H1WWqy1O7F$^pszCzCO#N>_0d1T2fx4BY1raA>Fj6DoZt2PJDoo_ zt(y|KwxstzU<8ZgcYx=>0q{D=daXxr)?+is{ua0m+y_1j7E+FYZ2witt03FoO8F~@lEm*I zLW?`#OWU+&pux2gf5h7mtD5hoFufB~_8ybZUCvH>#|Pu2H_Hv7wcb zow`eOl*p*M)Z(CY1H2El3_Q1vyk=hbp_`_KNuD6!kXsWq1&O%r)&j-JStg~brP9jJ;QC^<)vFkX{&3*2(?zOm+3#!f9P&oPM4iI;NuW`$OwV5WaMY& zwMNZVULO^!ZUAX>ALb8Rd+yqCWn~#yhrFU+!m-QJo`N!&W3@1FYt~^usOv0wjby2F zJ$=-C%<`e5LQPV;^E~4*Y$-!8hdGs(xP<0V*`07O8a(mcfebKCl zF2iay!vcp*Y?Dt^bOo6a3xhzO)xy%UAy(A?, 2013. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: 0.18\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-10-25 15:45+0400\n" -"PO-Revision-Date: 2013-03-17 19:30+CET\n" -"Last-Translator: Rostislav Grigoriev \n" -"Language-Team: \n" -"Language: Russian\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" - -#: __init__.py:55 -msgid "You supplied a target_object that's not an instance of a django Model." -msgstr "Указанный вами target_object не экземляр django-модели" - -#: models.py:18 -msgid "unique key" -msgstr "уникальный ключ" - -#: models.py:23 -msgid "verbose name" -msgstr "наименование" - -#: models.py:34 -msgid "type" -msgstr "тип" - -#: models.py:35 -msgid "types" -msgstr "типы" - -#: models.py:47 -msgid "interval" -msgstr "интервал" - -#: models.py:52 -#, python-format -msgid "Settings for %s" -msgstr "Настройки для %s" - -#: models.py:57 models.py:58 -msgid "settings" -msgstr "настройки" - -#: models.py:68 -msgid "Leave this blank to subscribe to any kind of object" -msgstr "Оставьте это поле пустым, чтобы подписаться на любой объект" - -#: models.py:78 -#, python-format -msgid "Subscription for: %s" -msgstr "Подписка для: %s" - -#: models.py:83 -msgid "subscription" -msgstr "подписка" - -#: models.py:84 -msgid "subscriptions" -msgstr "подписки" - -#: models.py:96 -msgid "link for notification" -msgstr "ссылка уведомления" - -#: models.py:106 -msgid "occurrences" -msgstr "вхождения" - -#: models.py:108 -msgid "" -"If the same notification was fired multiple times with no intermediate " -"notifications" -msgstr "" -"Если уведомление было вызвано несколько раз без промежуточного уведомления." - -#: models.py:170 -msgid "notification" -msgstr "уведомление" - -#: models.py:171 -msgid "notifications" -msgstr "уведомления" - -#: settings.py:19 -msgid "You have new notifications" -msgstr "Нет новых уведомлений" - -#: settings.py:38 -msgid "instantly" -msgstr "немедленно" - -#: settings.py:39 -msgid "daily" -msgstr "раз в день" - -#: settings.py:40 -msgid "weekly" -msgstr "раз в неделю" - -#: views.py:32 -#, python-format -msgid "%d times" -msgstr "%d раз" - -#: templates/emails/notification_email_message.txt:1 -#, python-format -msgid "Dear %(username)s," -msgstr "Дорогой %(username)s," - -#: templates/emails/notification_email_message.txt:3 -#, python-format -msgid "These are the %(digest)s notifications from %(site)s." -msgstr " Для вас %(digest)s уведомлений от %(site)s." - -#: templates/emails/notification_email_message.txt:9 -msgid "Thanks for using our site!" -msgstr "Спасибо, что пользуетесь сайтом!" - -#: templates/emails/notification_email_message.txt:11 -msgid "Sincerely" -msgstr "Искренне" - -#: templates/emails/notification_email_subject.txt:2 -#, python-format -msgid " %(digest)s Notifications %(site)s." -msgstr " %(digest)s уведомлений от %(site)s." diff --git a/django_notify/management/__init__.py b/django_notify/management/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/django_notify/management/commands/__init__.py b/django_notify/management/commands/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/django_notify/management/commands/notifymail.py b/django_notify/management/commands/notifymail.py deleted file mode 100644 index 714823e18..000000000 --- a/django_notify/management/commands/notifymail.py +++ /dev/null @@ -1,159 +0,0 @@ -from __future__ import print_function -from __future__ import unicode_literals -import os -import sys -import time -from datetime import datetime -from optparse import make_option - -from django_notify import settings as notify_settings - -from django.contrib.sites.models import Site -from django.core import mail -from django.core.management.base import BaseCommand -from django.template.loader import render_to_string -from django.utils.translation import ugettext as _, activate, deactivate -from django.conf import settings -from django_notify import models - -import smtplib - -import logging - - -class Command(BaseCommand): - can_import_settings = True - help = 'Sends notification emails to subscribed users taking into account the subscription interval' #@ReservedAssignment - option_list = BaseCommand.option_list + ( - make_option('--daemon','-d', - action='store_true', - dest='daemon', - default=False, - help='Go to daemon mode and exit'), - ) - - def _send_user_notifications(self, context, connection): - subject = _(notify_settings.EMAIL_SUBJECT) - message = render_to_string( - 'emails/notification_email_message.txt', - context - ) - email = mail.EmailMessage( - subject, message, notify_settings.EMAIL_SENDER, - [context['user'].email], connection=connection - ) - self.logger.info("Sending to: %s" % context['user'].email) - email.send(fail_silently=False) - - def handle(self, *args, **options): - # activate the language - activate(settings.LANGUAGE_CODE) - - daemon = options['daemon'] - - self.logger = logging.getLogger('django_notify') - - if not self.logger.handlers: - if daemon: - handler = logging.FileHandler(filename=notify_settings.NOTIFY_LOG) - else: - handler = logging.StreamHandler(self.stdout) - self.logger.addHandler(handler) - self.logger.setLevel(logging.INFO) - - self.logger.info("Starting django_notify e-mail dispatcher") - - if not notify_settings.SEND_EMAILS: - print("E-mails disabled - quitting.") - sys.exit() - - - - # Run as daemon, ie. fork the process - if daemon: - self.logger.info("Daemon mode enabled, forking") - try: - fpid = os.fork() - if fpid > 0: - # Running as daemon now. PID is fpid - self.logger.info("PID: %s" % str(fpid)) - pid_file = file(notify_settings.NOTIFY_PID, "w") - pid_file.write(str(fpid)) - pid_file.close() - sys.exit(0) - except OSError as e: - sys.stderr.write("fork failed: %d (%s)\n" % (e.errno, e.strerror)) - sys.exit(1) - - try: - self.send_loop() - except KeyboardInterrupt: - print("\nQuitting...") - - # deactivate the language - deactivate() - - def send_loop(self): - - # This could be /improved by looking up the last notified person - last_sent = None - context = {'user': None, - 'notifications': None, - 'digest': None, - 'site': Site.objects.get_current()} - - #create a connection to smtp server for reuse - try: - connection = mail.get_connection() - except: - self.logger.error("Could get a mail connection") - raise - - while True: - try: - connection.open() - except: - self.logger.error("Could not use e-mail connection") - raise - started_sending_at = datetime.now() - - self.logger.info("Starting send loop at %s" % str(started_sending_at)) - - if last_sent: - settings = models.Settings.objects.filter( - interval__lte=((started_sending_at-last_sent).seconds // 60) // 60 - ).order_by('user') - else: - settings = models.Settings.objects.all().order_by('user') - - for setting in settings: - context['user'] = setting.user - context['notifications']= [] - #get the index of the tuple corresponding to the interval and get the string name - context['digest'] = notify_settings.INTERVALS[[y[0] for y in notify_settings.INTERVALS].index(setting.interval)][1] - for subscription in setting.subscription_set.filter( - send_emails=True, - latest__is_emailed=False - ): - context['notifications'].append(subscription.latest) - if len(context['notifications']) > 0: - try: - self._send_user_notifications(context, connection) - for n in context['notifications']: - n.is_emailed=True - n.save() - except smtplib.SMTPException: - # TODO: Only quit on certain errors, retry on others. - self.logger.error("You have an error with your SMTP server connection, quitting.") - raise - - connection.close() - last_sent = datetime.now() - elapsed_seconds = (last_sent - started_sending_at).seconds - time.sleep( - max( - (min(notify_settings.INTERVALS)[0] - elapsed_seconds) * 60, - notify_settings.NOTIFY_SLEEP_TIME, - 0 - ) - ) diff --git a/django_notify/migrations/0001_initial.py b/django_notify/migrations/0001_initial.py deleted file mode 100644 index 53df95afb..000000000 --- a/django_notify/migrations/0001_initial.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -try: - from django.contrib.auth import get_user_model -except ImportError: # django < 1.5 - from django.contrib.auth.models import User -else: - User = get_user_model() - -user_orm_label = '%s.%s' % (User._meta.app_label, User._meta.object_name) -user_model_label = '%s.%s' % (User._meta.app_label, User._meta.module_name) - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'NotificationType' - db.create_table('notify_notificationtype', ( - ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128, primary_key=True)), - ('label', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)), - ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'], null=True, blank=True)), - )) - db.send_create_signal('django_notify', ['NotificationType']) - - # Adding model 'Settings' - db.create_table('notify_settings', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm[user_orm_label])), - ('interval', self.gf('django.db.models.fields.SmallIntegerField')(default=0)), - )) - db.send_create_signal('django_notify', ['Settings']) - - # Adding model 'Subscription' - db.create_table('notify_subscription', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('settings', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['django_notify.Settings'])), - ('notification_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['django_notify.NotificationType'])), - ('object_id', self.gf('django.db.models.fields.CharField')(max_length=64, null=True, blank=True)), - ('send_emails', self.gf('django.db.models.fields.BooleanField')(default=True)), - )) - db.send_create_signal('django_notify', ['Subscription']) - - # Adding model 'Notification' - db.create_table('notify_notification', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('subscription', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['django_notify.Subscription'], null=True, on_delete=models.SET_NULL, blank=True)), - ('message', self.gf('django.db.models.fields.TextField')()), - ('url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True, blank=True)), - ('is_viewed', self.gf('django.db.models.fields.BooleanField')(default=False)), - ('is_emailed', self.gf('django.db.models.fields.BooleanField')(default=False)), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal('django_notify', ['Notification']) - - - def backwards(self, orm): - # Deleting model 'NotificationType' - db.delete_table('notify_notificationtype') - - # Deleting model 'Settings' - db.delete_table('notify_settings') - - # Deleting model 'Subscription' - db.delete_table('notify_subscription') - - # Deleting model 'Notification' - db.delete_table('notify_notification') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - user_model_label: { - 'Meta': {'object_name': User.__name__, 'db_table': "'%s'" % User._meta.db_table}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'django_notify.notification': { - 'Meta': {'object_name': 'Notification', 'db_table': "'notify_notification'"}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'message': ('django.db.models.fields.TextField', [], {}), - 'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Subscription']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), - 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.notificationtype': { - 'Meta': {'object_name': 'NotificationType', 'db_table': "'notify_notificationtype'"}, - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.settings': { - 'Meta': {'object_name': 'Settings', 'db_table': "'notify_settings'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'interval': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_orm_label}) - }, - 'django_notify.subscription': { - 'Meta': {'object_name': 'Subscription', 'db_table': "'notify_subscription'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'notification_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.NotificationType']"}), - 'object_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'settings': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Settings']"}) - } - } - - complete_apps = ['django_notify'] diff --git a/django_notify/migrations/0002_auto__add_field_notification_occurrences.py b/django_notify/migrations/0002_auto__add_field_notification_occurrences.py deleted file mode 100644 index 25dd8073b..000000000 --- a/django_notify/migrations/0002_auto__add_field_notification_occurrences.py +++ /dev/null @@ -1,103 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -try: - from django.contrib.auth import get_user_model -except ImportError: # django < 1.5 - from django.contrib.auth.models import User -else: - User = get_user_model() - -user_orm_label = '%s.%s' % (User._meta.app_label, User._meta.object_name) -user_model_label = '%s.%s' % (User._meta.app_label, User._meta.module_name) - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding field 'Notification.occurrences' - db.add_column('notify_notification', 'occurrences', - self.gf('django.db.models.fields.PositiveIntegerField')(default=1), - keep_default=False) - - - def backwards(self, orm): - # Deleting field 'Notification.occurrences' - db.delete_column('notify_notification', 'occurrences') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - user_model_label: { - 'Meta': {'object_name': User.__name__, 'db_table': "'%s'" % User._meta.db_table}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'django_notify.notification': { - 'Meta': {'ordering': "('-id',)", 'object_name': 'Notification', 'db_table': "'notify_notification'"}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'message': ('django.db.models.fields.TextField', [], {}), - 'occurrences': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), - 'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Subscription']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), - 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.notificationtype': { - 'Meta': {'object_name': 'NotificationType', 'db_table': "'notify_notificationtype'"}, - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.settings': { - 'Meta': {'object_name': 'Settings', 'db_table': "'notify_settings'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'interval': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_orm_label}) - }, - 'django_notify.subscription': { - 'Meta': {'object_name': 'Subscription', 'db_table': "'notify_subscription'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'notification_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.NotificationType']"}), - 'object_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'settings': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Settings']"}) - } - } - - complete_apps = ['django_notify'] \ No newline at end of file diff --git a/django_notify/migrations/0003_auto__add_field_subscription_latest.py b/django_notify/migrations/0003_auto__add_field_subscription_latest.py deleted file mode 100644 index 0012ac696..000000000 --- a/django_notify/migrations/0003_auto__add_field_subscription_latest.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -try: - from django.contrib.auth import get_user_model -except ImportError: # django < 1.5 - from django.contrib.auth.models import User -else: - User = get_user_model() - -user_orm_label = '%s.%s' % (User._meta.app_label, User._meta.object_name) -user_model_label = '%s.%s' % (User._meta.app_label, User._meta.module_name) - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding field 'Subscription.latest' - db.add_column('notify_subscription', 'latest', - self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='latest_for', null=True, to=orm['django_notify.Notification']), - keep_default=False) - - - def backwards(self, orm): - # Deleting field 'Subscription.latest' - db.delete_column('notify_subscription', 'latest_id') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - user_model_label: { - 'Meta': {'object_name': User.__name__, 'db_table': "'%s'" % User._meta.db_table}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'django_notify.notification': { - 'Meta': {'ordering': "('-id',)", 'object_name': 'Notification', 'db_table': "'notify_notification'"}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'message': ('django.db.models.fields.TextField', [], {}), - 'occurrences': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), - 'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Subscription']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), - 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.notificationtype': { - 'Meta': {'object_name': 'NotificationType', 'db_table': "'notify_notificationtype'"}, - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.settings': { - 'Meta': {'object_name': 'Settings', 'db_table': "'notify_settings'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'interval': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_orm_label}) - }, - 'django_notify.subscription': { - 'Meta': {'object_name': 'Subscription', 'db_table': "'notify_subscription'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'latest': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'latest_for'", 'null': 'True', 'to': "orm['django_notify.Notification']"}), - 'notification_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.NotificationType']"}), - 'object_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'settings': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Settings']"}) - } - } - - complete_apps = ['django_notify'] diff --git a/django_notify/migrations/0004_auto__chg_field_notification_url.py b/django_notify/migrations/0004_auto__chg_field_notification_url.py deleted file mode 100644 index 09f6eb186..000000000 --- a/django_notify/migrations/0004_auto__chg_field_notification_url.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -try: - from django.contrib.auth import get_user_model -except ImportError: # django < 1.5 - from django.contrib.auth.models import User -else: - User = get_user_model() - -user_orm_label = '%s.%s' % (User._meta.app_label, User._meta.object_name) -user_model_label = '%s.%s' % (User._meta.app_label, User._meta.module_name) - - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Changing field 'Notification.url' - db.alter_column('notify_notification', 'url', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)) - - def backwards(self, orm): - - # Changing field 'Notification.url' - db.alter_column('notify_notification', 'url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True)) - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - user_model_label: { - 'Meta': {'object_name': User.__name__, 'db_table': "'%s'" % User._meta.db_table}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'django_notify.notification': { - 'Meta': {'ordering': "('-id',)", 'object_name': 'Notification', 'db_table': "'notify_notification'"}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_emailed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'message': ('django.db.models.fields.TextField', [], {}), - 'occurrences': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), - 'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Subscription']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), - 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.notificationtype': { - 'Meta': {'object_name': 'NotificationType', 'db_table': "'notify_notificationtype'"}, - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'primary_key': 'True'}), - 'label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}) - }, - 'django_notify.settings': { - 'Meta': {'object_name': 'Settings', 'db_table': "'notify_settings'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'interval': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_orm_label}) - }, - 'django_notify.subscription': { - 'Meta': {'object_name': 'Subscription', 'db_table': "'notify_subscription'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'latest': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'latest_for'", 'null': 'True', 'to': "orm['django_notify.Notification']"}), - 'notification_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.NotificationType']"}), - 'object_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}), - 'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'settings': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Settings']"}) - } - } - - complete_apps = ['django_notify'] diff --git a/django_notify/migrations/__init__.py b/django_notify/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/django_notify/models.py b/django_notify/models.py deleted file mode 100644 index 6c29fba5b..000000000 --- a/django_notify/models.py +++ /dev/null @@ -1,173 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from django.db import models -from django.db.models import Q -from django.utils.translation import ugettext_lazy as _ - -from django.contrib.contenttypes.models import ContentType - -from django_notify import settings - -class NotificationType(models.Model): - """ - Notification types are added on-the-fly by the - applications adding new notifications - """ - key = models.CharField( - max_length=128, - primary_key=True, - verbose_name=_('unique key'), - unique=True - ) - label = models.CharField( - max_length=128, - verbose_name=_('verbose name'), - blank=True, - null=True - ) - content_type = models.ForeignKey(ContentType, blank=True, null=True) - - def __unicode__(self): - return self.key - - class Meta: - db_table = settings.DB_TABLE_PREFIX + '_notificationtype' - verbose_name = _('type') - verbose_name_plural = _('types') - -class Settings(models.Model): - """ - Reusable settings object for a subscription - """ - - user = models.ForeignKey( - settings.USER_MODEL - ) - interval = models.SmallIntegerField( - choices=settings.INTERVALS, - verbose_name=_('interval'), - default=settings.INTERVALS_DEFAULT - ) - - def __unicode__(self): - obj_name = _("Settings for %s") % self.user.username - return unicode(obj_name) - - class Meta: - db_table = settings.DB_TABLE_PREFIX + '_settings' - verbose_name = _('settings') - verbose_name_plural = _('settings') - -class Subscription(models.Model): - - settings = models.ForeignKey(Settings) - notification_type = models.ForeignKey(NotificationType) - object_id = models.CharField( - max_length=64, - null=True, - blank=True, - help_text=_('Leave this blank to subscribe to any kind of object') - ) - send_emails = models.BooleanField(default=True) - latest = models.ForeignKey('Notification', - null=True, - blank=True, - related_name='latest_for' - ) - - def __unicode__(self): - obj_name = _("Subscription for: %s") % str(self.settings.user.username) - return unicode(obj_name) - - class Meta: - db_table = settings.DB_TABLE_PREFIX + '_subscription' - verbose_name = _('subscription') - verbose_name_plural = _('subscriptions') - -class Notification(models.Model): - - subscription = models.ForeignKey( - Subscription, - null=True, - blank=True, - on_delete=models.SET_NULL - ) - message = models.TextField() - url = models.CharField( - verbose_name=_('link for notification'), - blank=True, - null=True, - max_length=200 - ) - is_viewed = models.BooleanField(default=False) - is_emailed = models.BooleanField(default=False) - created = models.DateTimeField(auto_now_add=True) - occurrences = models.PositiveIntegerField( - default=1, - verbose_name=_('occurrences'), - help_text=_( - 'If the same notification was fired multiple ' - 'times with no intermediate notifications' - ) - ) - - @classmethod - def create_notifications(cls, key, **kwargs): - if not key or not isinstance(key, str): - raise KeyError('No notification key (string) specified.') - - object_id = kwargs.pop('object_id', None) - filter_exclude = kwargs.pop('filter_exclude', {}) - - objects_created = [] - subscriptions = Subscription.objects.filter( - Q(notification_type__key=key) | - Q(notification_type__key=None), - **filter_exclude - ) - if object_id: - subscriptions = subscriptions.filter( - Q(object_id=object_id) | - Q(object_id=None) - ) - - subscriptions = subscriptions.prefetch_related('latest', 'settings') - subscriptions = subscriptions.order_by('settings__user') - prev_user = None - - for subscription in subscriptions: - # Don't alert the same user several times even though overlapping - # subscriptions occur. - if subscription.settings.user == prev_user: - continue - - # Check if it's the same as the previous message - latest = subscription.latest - if latest and (latest.message == kwargs.get('message', None) and - latest.url == kwargs.get('url', None) and - latest.is_viewed == False): - # Both message and URL are the same, and it hasn't been viewed - # so just increment occurrence count. - latest.occurrences = latest.occurrences + 1 - latest.is_emailed = False - latest.save() - else: - # Insert a new notification - new_obj = cls.objects.create(subscription=subscription, **kwargs) - objects_created.append( - new_obj - ) - subscription.latest = new_obj - subscription.save() - prev_user = subscription.settings.user - - return objects_created - - def __unicode__(self): - return "%s: %s" % (str(self.subscription.settings.user), self.message) - - class Meta: - db_table = settings.DB_TABLE_PREFIX + '_notification' - verbose_name = _('notification') - verbose_name_plural = _('notifications') - ordering = ('-id',) diff --git a/django_notify/settings.py b/django_notify/settings.py deleted file mode 100644 index ad605ed19..000000000 --- a/django_notify/settings.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import unicode_literals -from django.conf import settings as django_settings -from django import VERSION as DJANGO_VERSION -from django.utils.translation import ugettext_lazy as _ - - -DB_TABLE_PREFIX = 'notify' - -# You need to switch this setting on, otherwise nothing will happen :) -ENABLED = getattr(django_settings, 'NOTIFY_ENABLED', True) - -# Enable django-admin registration -ENABLE_ADMIN = getattr(django_settings, 'NOTIFY_ENABLE_ADMIN', False) - -# Email notifications won't get sent unless you run -# python manage.py notifymail -SEND_EMAILS = getattr(django_settings, 'NOTIFY_SEND_EMAILS', True) - -EMAIL_SUBJECT = getattr(django_settings, - 'NOTIFY_EMAIL_SUBJECT', _("You have new notifications")) - -EMAIL_SENDER = getattr(django_settings, - 'NOTIFY_EMAIL_SENDER', "notifications@example.com") - -# Seconds to sleep between each database poll -# (leave high unless you really want to send extremely real time -# notifications) -NOTIFY_SLEEP_TIME = 120 - -# You can always make up more numbers... they simply identify which notifications -# to send when invoking the script, and the number indicates how many hours -# to minimum pass between each notification. -INSTANTLY = 0 -DAILY = (24 - 1) * 60 # Subtract 1, because the job finishes less than 24h before the next... -WEEKLY = 7 * (24 - 1) * 60 - -# List of intervals available. In minutes -INTERVALS = getattr(django_settings, 'NOTIFY_INTERVALS', - [(INSTANTLY, _('instantly')), - (DAILY, _('daily')), - (WEEKLY, _('weekly'))] -) - -INTERVALS_DEFAULT = INSTANTLY - -# Django 1.5+ -if DJANGO_VERSION >= (1,5): - USER_MODEL = getattr(django_settings, 'AUTH_USER_MODEL', 'auth.User') -else: - USER_MODEL = 'auth.User' - -#################### -# PLANNED SETTINGS # -#################### - -# Minimum logging and digital garbage! Don't save too much crap! - -# After how many days should viewed notifications be deleted? -AUTO_DELETE = getattr(django_settings, 'NOTIFY_AUTO_DELETE', 120) - -# After how many days should all types of notifications be deleted? -AUTO_DELETE_ALL = getattr(django_settings, 'NOTIFY_AUTO_DELETE_ALL', 120) - -NOTIFY_LOG = getattr(django_settings, 'NOTIFY_LOG', '/tmp/daemon_notify.log') - -NOTIFY_PID = getattr(django_settings, 'NOTIFY_PID', '/tmp/daemon_notify.pid') diff --git a/django_notify/templates/emails/notification_email_message.txt b/django_notify/templates/emails/notification_email_message.txt deleted file mode 100644 index d07f7bf26..000000000 --- a/django_notify/templates/emails/notification_email_message.txt +++ /dev/null @@ -1,13 +0,0 @@ -{% load i18n %}{% autoescape off %}{% load url from future %}{% blocktrans with user.username as username %}Dear {{ username }},{% endblocktrans %} - -{% blocktrans with site.name as site %}These are the {{ digest }} notifications from {{ site }}.{% endblocktrans %} -{% for n in notifications %} - * {{ n.message }} - http://{{ site.domain }}{{n.url}} -{% endfor %} - -{% trans "Thanks for using our site!" %} - -{% trans "Sincerely" %}, -{{ site.name }} -{% endautoescape %} diff --git a/django_notify/templates/emails/notification_email_subject.txt b/django_notify/templates/emails/notification_email_subject.txt deleted file mode 100644 index 91f409be1..000000000 --- a/django_notify/templates/emails/notification_email_subject.txt +++ /dev/null @@ -1,2 +0,0 @@ -{% load i18n %} -{% blocktrans with site.name as site %} {{ digest }} Notifications {{ site }}.{% endblocktrans %} diff --git a/django_notify/tests.py b/django_notify/tests.py deleted file mode 100644 index 501deb776..000000000 --- a/django_notify/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -This file demonstrates writing tests using the unittest module. These will pass -when you run "manage.py test". - -Replace this with more appropriate tests for your application. -""" - -from django.test import TestCase - - -class SimpleTest(TestCase): - def test_basic_addition(self): - """ - Tests that 1 + 1 always equals 2. - """ - self.assertEqual(1 + 1, 2) diff --git a/django_notify/urls.py b/django_notify/urls.py deleted file mode 100644 index 02a291c4f..000000000 --- a/django_notify/urls.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from django.conf.urls import patterns, url - -urlpatterns = patterns('', - url('^json/get/$', 'django_notify.views.get_notifications', name='json_get'), - url('^json/get/(?P\d+)/$', 'django_notify.views.get_notifications', name='json_get'), - url('^json/mark-read/$', 'django_notify.views.mark_read', name='json_mark_read_base'), - url('^json/mark-read/(\d+)/$', 'django_notify.views.mark_read', name='json_mark_read'), - url('^json/mark-read/(?P\d+)/(?P\d+)/$', 'django_notify.views.mark_read', name='json_mark_read'), - url('^goto/(?P\d+)/$', 'django_notify.views.goto', name='goto'), - url('^goto/$', 'django_notify.views.goto', name='goto_base'), -) - -def get_pattern(app_name="notify", namespace="notify"): - """Every url resolution takes place as "notify:view_name". - https://docs.djangoproject.com/en/dev/topics/http/urls/#topics-http-reversing-url-namespaces - """ - return urlpatterns, app_name, namespace \ No newline at end of file diff --git a/django_notify/views.py b/django_notify/views.py deleted file mode 100644 index d251c4d49..000000000 --- a/django_notify/views.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from django_notify.decorators import json_view, login_required_ajax -from django_notify import models -from django.contrib.auth.decorators import login_required -from django.shortcuts import redirect, get_object_or_404 -from django.utils.translation import ugettext as _ - -@login_required_ajax -@json_view -def get_notifications(request, latest_id=None, is_viewed=False, max_results=10): - - notifications = models.Notification.objects.filter(subscription__settings__user=request.user,) - - if not is_viewed is None: - notifications = notifications.filter(is_viewed=is_viewed) - - if not latest_id is None: - notifications = notifications.filter(id__gt=latest_id) - - notifications = notifications.order_by('-id') - notifications = notifications.prefetch_related('subscription', 'subscription__notification_type') - - from django.contrib.humanize.templatetags.humanize import naturaltime - - return {'success': True, - 'total_count': notifications.count(), - 'objects': [{'pk': n.pk, - 'message': n.message, - 'url': n.url, - 'occurrences': n.occurrences, - 'occurrences_msg': _('%d times') % n.occurrences, - 'type': n.subscription.notification_type.key, - 'since': naturaltime(n.created)} for n in notifications[:max_results]]} - -@login_required -def goto(request, notification_id=None): - referer = request.META.get('HTTP_REFERER', '') - if not notification_id: - return redirect(referer) - notification = get_object_or_404(models.Notification, - subscription__settings__user=request.user, - id=notification_id) - notification.is_viewed=True - notification.save() - if not notification.url is None: - return redirect(notification.url) - return redirect(referer) - -@login_required_ajax -@json_view -def mark_read(request, id_lte, notification_type_id=None, id_gte=None): - - notifications = models.Notification.objects.filter(subscription__settings__user=request.user, - id__lte=id_lte) - - if notification_type_id: - notifications = notifications.filter(notification_type__id=notification_type_id) - - if id_gte: - notifications = notifications.filter(id__gte=id_gte) - - notifications.update(is_viewed=True) - - return {'success': True} diff --git a/docs/installation.rst b/docs/installation.rst index 2d1d888d9..1dc2b3858 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -101,7 +101,7 @@ maintain the order due to database relational constraints: 'django.contrib.sites', # django 1.6.2 'django.contrib.humanize', 'south', - 'django_notify', + 'django_nyt', 'mptt', 'sekizai', 'sorl.thumbnail', @@ -165,9 +165,9 @@ following lines at the end of your project's ``urls.py``. :: from wiki.urls import get_pattern as get_wiki_pattern - from django_notify.urls import get_pattern as get_notify_pattern + from django_nyt.urls import get_pattern as get_nyt_pattern urlpatterns += patterns('', - (r'^notify/', get_notify_pattern()), + (r'^notifications/', get_nyt_pattern()), (r'', get_wiki_pattern()) ) diff --git a/requirements.txt b/requirements.txt index c550107a1..b855f3aed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ django>=1.4 -South Markdown>2.2.0 django-sekizai>=0.7 django-mptt>=0.5.3 -sorl-thumbnail==11.12.1b +sorl-thumbnail>=11.12.1b Pillow six +# Notification system +django-nyt>=0.9 \ No newline at end of file diff --git a/runtests.py b/runtests.py index 42540db3d..fd1eaee79 100755 --- a/runtests.py +++ b/runtests.py @@ -22,7 +22,7 @@ 'django.contrib.humanize', 'django.contrib.sites', 'south', - 'django_notify', + 'django_nyt', 'mptt', 'sekizai', 'sorl.thumbnail', diff --git a/setup.py b/setup.py index 28496ff46..c879771b4 100755 --- a/setup.py +++ b/setup.py @@ -8,19 +8,37 @@ # Used for the long_description. It's nice, because now 1) we have a top level # README file and 2) it's easier to type in the README file than to put a raw # string in below ... + + def get_path(fname): return os.path.join(os.path.dirname(__file__), fname) + def read(fname): return open(get_path(fname)).read() + +def dynamic_requirements(requirements): + + try: + from django import VERSION + if VERSION < (1, 7): + requirements.append("South>=0.8,!=0.8.3") + except ImportError: + # No django so assuming that a new one will + # get installed... + pass + return requirements + + packages = find_packages() try: import pypandoc long_description = pypandoc.convert(get_path('README.md'), 'rst') - long_description = long_description.split('')[0] + long_description = long_description.split( + '')[0] f = open(get_path('README.rst'), 'w') f.write(long_description) f.close() @@ -29,18 +47,18 @@ def read(fname): long_description = "" setup( - name = "wiki", - version = VERSION, - author = "Benjamin Bach", - author_email = "benjamin@overtag.dk", - url = "http://www.django-wiki.org", - description = "A wiki system written for the Django framework.", - license = "GPLv3", - keywords = "django wiki markdown", - packages=find_packages(exclude=["testproject","testproject.*"]), - #long_description=long_description, + name="wiki", + version=VERSION, + author="Benjamin Bach", + author_email="benjamin@overtag.dk", + url="http://www.django-wiki.org", + description="A wiki system written for the Django framework.", + license="GPLv3", + keywords="django wiki markdown", + packages=find_packages(exclude=["testproject", "testproject.*"]), + # long_description=long_description, zip_safe=False, - install_requires=read('requirements.txt').split("\n"), + install_requires=dynamic_requirements(read('requirements.txt').split("\n")), classifiers=[ 'Development Status :: 3 - Alpha', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', diff --git a/testproject/django_notify b/testproject/django_notify deleted file mode 120000 index 4451cc83c..000000000 --- a/testproject/django_notify +++ /dev/null @@ -1 +0,0 @@ -../django_notify \ No newline at end of file diff --git a/testproject/testproject/db/prepopulated-customauthuser.db b/testproject/prepopulated.db similarity index 73% rename from testproject/testproject/db/prepopulated-customauthuser.db rename to testproject/prepopulated.db index f4e6ff942b78c56e85652ee136a50fcc49534f7a..db314c44ac654e36ebdec3d673ace69d55c2a1db 100644 GIT binary patch delta 9623 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 fh^jSw>dD;ng|~m literal 130048 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 vR^!zNv&eNx-Sb6sQ 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 J78hnp{~tj?zGVOa diff --git a/testproject/testproject/settings/__init__.py b/testproject/testproject/settings/__init__.py index f2b29ff6b..30ffaa12e 100644 --- a/testproject/testproject/settings/__init__.py +++ b/testproject/testproject/settings/__init__.py @@ -14,7 +14,7 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': os_path.join(PROJECT_PATH, 'db', 'prepopulated.db'), # Or path to database file if using sqlite3. + 'NAME': 'prepopulated.db', # Or path to database file if using sqlite3. } } @@ -91,10 +91,9 @@ 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', - 'south', 'sekizai', 'sorl.thumbnail', - 'django_notify', + 'django_nyt', 'wiki', 'wiki.plugins.macros', 'wiki.plugins.help', @@ -105,6 +104,15 @@ 'mptt', ] +from django import VERSION +if VERSION <= (1, 6): + INSTALLED_APPS.append('south') + SOUTH_MIGRATION_MODULES = { + 'django_nyt': 'django_nyt.south_migrations', + } +else: + TEST_RUNNER = 'django.test.runner.DiscoverRunner' + # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to # the site admins on every HTTP 500 error when DEBUG=False. @@ -162,4 +170,3 @@ from testproject.settings.local import * except ImportError: pass - diff --git a/testproject/testproject/urls.py b/testproject/testproject/urls.py index c9251c80b..4af75e022 100644 --- a/testproject/testproject/urls.py +++ b/testproject/testproject/urls.py @@ -19,7 +19,7 @@ ) from wiki.urls import get_pattern as get_wiki_pattern -from django_notify.urls import get_pattern as get_notify_pattern +from django_nyt.urls import get_pattern as get_notify_pattern urlpatterns += patterns('', (r'^notify/', get_notify_pattern()), (r'', get_wiki_pattern()) diff --git a/wiki/__init__.py b/wiki/__init__.py index ce250522b..7dc1e8fc2 100644 --- a/wiki/__init__.py +++ b/wiki/__init__.py @@ -15,4 +15,4 @@ # You should have received a copy of the GNU General Public License # along with django-wiki. If not, see . -VERSION = "0.0.23" +VERSION = "0.0.3" diff --git a/wiki/core/compat.py b/wiki/core/compat.py index 3a0f9ac80..fb1f9009e 100644 --- a/wiki/core/compat.py +++ b/wiki/core/compat.py @@ -4,7 +4,7 @@ from django.db import transaction from django.conf import settings as django_settings - + # Django 1.5+ if DJANGO_VERSION >= (1,5): USER_MODEL = getattr(django_settings, 'AUTH_USER_MODEL', 'auth.User') diff --git a/wiki/models/__init__.py b/wiki/models/__init__.py index 4175d6d84..21eb7148b 100644 --- a/wiki/models/__init__.py +++ b/wiki/models/__init__.py @@ -22,8 +22,8 @@ if not 'sekizai' in django_settings.INSTALLED_APPS: raise ImproperlyConfigured('django-wiki: needs sekizai in INSTALLED_APPS') -if not 'django_notify' in django_settings.INSTALLED_APPS: - raise ImproperlyConfigured('django-wiki: needs django_notify in INSTALLED_APPS') +#if not 'django_nyt' in django_settings.INSTALLED_APPS: +# raise ImproperlyConfigured('django-wiki: needs django_nyt in INSTALLED_APPS') if not 'django.contrib.humanize' in django_settings.INSTALLED_APPS: raise ImproperlyConfigured('django-wiki: needs django.contrib.humanize in INSTALLED_APPS') diff --git a/wiki/plugins/images/migrations/0001_initial.py b/wiki/plugins/images/migrations/0001_initial.py index 9d9f273eb..f08fab889 100644 --- a/wiki/plugins/images/migrations/0001_initial.py +++ b/wiki/plugins/images/migrations/0001_initial.py @@ -1,122 +1,20 @@ # -*- coding: utf-8 -*- -import datetime +from south.utils import datetime_utils as datetime from south.db import db from south.v2 import SchemaMigration from django.db import models -try: - from django.contrib.auth import get_user_model -except ImportError: # django < 1.5 - from django.contrib.auth.models import User -else: - User = get_user_model() - -user_orm_label = '%s.%s' % (User._meta.app_label, User._meta.object_name) -user_model_label = '%s.%s' % (User._meta.app_label, User._meta.module_name) - - class Migration(SchemaMigration): def forwards(self, orm): - # Adding model 'Image' - db.create_table('images_image', ( - ('revisionplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wiki.RevisionPlugin'], unique=True, primary_key=True)), - ('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100)), - ('caption', self.gf('django.db.models.fields.CharField')(max_length=2056, null=True, blank=True)), - )) - db.send_create_signal('images', ['Image']) - + pass def backwards(self, orm): - # Deleting model 'Image' - db.delete_table('images_image') - + pass models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - user_model_label: { - 'Meta': {'object_name': User.__name__, 'db_table': "'%s'" % User._meta.db_table}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'images.image': { - 'Meta': {'object_name': 'Image', '_ormbases': ['wiki.RevisionPlugin']}, - 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), - 'revisionplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.RevisionPlugin']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'wiki.article': { - 'Meta': {'object_name': 'Article'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'current_set'", 'unique': 'True', 'null': 'True', 'to': "orm['wiki.ArticleRevision']"}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), - 'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_orm_label, 'null': 'True', 'blank': 'True'}) - }, - 'wiki.articleplugin': { - 'Meta': {'object_name': 'ArticlePlugin'}, - 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}), - 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'wiki.articlerevision': { - 'Meta': {'ordering': "('created',)", 'unique_together': "(('article', 'revision_number'),)", 'object_name': 'ArticleRevision'}, - 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}), - 'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), - 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.ArticleRevision']", 'null': 'True', 'blank': 'True'}), - 'redirect': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'redirect_set'", 'null': 'True', 'to': "orm['wiki.Article']"}), - 'revision_number': ('django.db.models.fields.IntegerField', [], {}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['%s']" % user_orm_label, 'null': 'True', 'blank': 'True'}), - 'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}) - }, - 'wiki.revisionplugin': { - 'Meta': {'object_name': 'RevisionPlugin', '_ormbases': ['wiki.ArticlePlugin']}, - 'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}), - 'revision': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.ArticleRevision']"}) - } + } complete_apps = ['images'] \ No newline at end of file diff --git a/wiki/plugins/notifications/forms.py b/wiki/plugins/notifications/forms.py index 7d268d013..a53e3a0ca 100644 --- a/wiki/plugins/notifications/forms.py +++ b/wiki/plugins/notifications/forms.py @@ -3,8 +3,8 @@ from django.forms.models import modelformset_factory, BaseModelFormSet from django.utils.translation import ugettext as _ -from django_notify.models import Settings, NotificationType -from django_notify import settings as notify_settings +from django_nyt.models import Settings, NotificationType +from django_nyt import settings as notify_settings from django.contrib.contenttypes.models import ContentType from django.utils.safestring import mark_safe diff --git a/wiki/plugins/notifications/models.py b/wiki/plugins/notifications/models.py index e60cb9214..316db6973 100644 --- a/wiki/plugins/notifications/models.py +++ b/wiki/plugins/notifications/models.py @@ -4,8 +4,8 @@ from django.utils.translation import ugettext_lazy as _ from django.db.models import signals -from django_notify import notify -from django_notify.models import Subscription +from django_nyt.utils import notify +from django_nyt.models import Subscription from wiki import models as wiki_models from wiki.models.pluginbase import ArticlePlugin diff --git a/wiki/plugins/notifications/settings.py b/wiki/plugins/notifications/settings.py index 6eae1be94..51bf6b18e 100644 --- a/wiki/plugins/notifications/settings.py +++ b/wiki/plugins/notifications/settings.py @@ -2,7 +2,7 @@ APP_LABEL = 'wiki' -# Key for django_notify - changing it will break any existing notifications. +# Key for django_nyt - changing it will break any existing notifications. ARTICLE_EDIT = "article_edit" SLUG = 'notifications' \ No newline at end of file diff --git a/wiki/plugins/notifications/templates/wiki/plugins/notifications/menubaritem.html b/wiki/plugins/notifications/templates/wiki/plugins/notifications/menubaritem.html index b8df40ef6..94f3ab4c1 100644 --- a/wiki/plugins/notifications/templates/wiki/plugins/notifications/menubaritem.html +++ b/wiki/plugins/notifications/templates/wiki/plugins/notifications/menubaritem.html @@ -17,9 +17,9 @@ {% addtoblock "js" %} {% endaddtoblock %} {% addtoblock "js" %}{% endaddtoblock %} diff --git a/wiki/templates/wiki/accounts/login.html b/wiki/templates/wiki/accounts/login.html index eac3dbced..9353a506d 100644 --- a/wiki/templates/wiki/accounts/login.html +++ b/wiki/templates/wiki/accounts/login.html @@ -4,23 +4,30 @@ {% block wiki_pagetitle %}wiki_pagetitle{% trans "Log in" %}{% endblock %} {% block wiki_contents %} -

{% trans "Please log in" %}

-
-{% wiki_form form %} -
-
-
- +
+
+
+

{% trans "Please log in" %}

+ + {% wiki_form form %} +
+
+
+ +
+
+ + +

+ {% trans "Don't have an account?" %} {% trans "Sign up" %} » +

+
+
- - -

- {% trans "Don't have an account?" %} {% trans "Sign up" %} » -

{% addtoblock "js" %}